Annotation of gpl/axl/src/axl_stream.c, revision 1.1
1.1 ! misho 1: /*
! 2: * LibAxl: Another XML library
! 3: * Copyright (C) 2006 Advanced Software Production Line, S.L.
! 4: *
! 5: * This program is free software; you can redistribute it and/or
! 6: * modify it under the terms of the GNU Lesser General Public License
! 7: * as published by the Free Software Foundation; either version 2.1 of
! 8: * the License, or (at your option) any later version.
! 9: *
! 10: * This program is distributed in the hope that it will be useful,
! 11: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 13: * GNU Lesser General Public License for more details.
! 14: *
! 15: * You should have received a copy of the GNU Lesser General Public
! 16: * License along with this program; if not, write to the Free
! 17: * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
! 18: * 02111-1307 USA
! 19: *
! 20: * You may find a copy of the license under this software is released
! 21: * at COPYING file. This is LGPL software: you are welcome to
! 22: * develop proprietary applications using this library without any
! 23: * royalty or fee but returning back any change, improvement or
! 24: * addition in the form of source code, project image, documentation
! 25: * patches, etc.
! 26: *
! 27: * For commercial support on build XML enabled solutions contact us:
! 28: *
! 29: * Postal address:
! 30: * Advanced Software Production Line, S.L.
! 31: * Edificio Alius A, Oficina 102,
! 32: * C/ Antonio Suarez Nº 10,
! 33: * Alcalá de Henares 28802 Madrid
! 34: * Spain
! 35: *
! 36: * Email address:
! 37: * info@aspl.es - http://www.aspl.es/xml
! 38: */
! 39:
! 40: #include <axl.h>
! 41: #include <math.h>
! 42:
! 43: #define LOG_DOMAIN "axl-stream"
! 44:
! 45: /**
! 46: * @internal
! 47: *
! 48: * @brief Basic checking which allows to test if the provided size
! 49: * could be satisfied by the current status of the provided stream.
! 50: *
! 51: * @param stream The stream where the operation will be performed.
! 52: *
! 53: * @param size The size to check.
! 54: *
! 55: * @return 1 if the size falls out side the stream limit or 0 it not.
! 56: */
! 57: #define fall_out_side_checking(stream, size) ((size + stream->stream_index) > stream->stream_size)
! 58:
! 59: /**
! 60: * @internal
! 61: *
! 62: * This is the size of the buffer allocated while using the axlStream
! 63: * as a representation of a streaming media, like a file
! 64: * descriptor. This value should contain a value using the (4k * n + 1).
! 65: *
! 66: * @param stream
! 67: */
! 68: #define STREAM_BUFFER_SIZE 8192
! 69:
! 70: /**
! 71: * @internal
! 72: *
! 73: * Internal definition used to represent the maximum value used for
! 74: * calls done to match a set of chunks.
! 75: */
! 76: #define MAX_INSPECTED_CHUNKS 30
! 77:
! 78: typedef enum {
! 79: STREAM_FD,
! 80: STREAM_MEM
! 81: }axlStreamType;
! 82:
! 83: struct _axlStream {
! 84:
! 85: /* current stream content */
! 86: char * stream;
! 87:
! 88: /* where the current stream is positioned. */
! 89: int stream_index;
! 90:
! 91: /* global index for the device being streamed */
! 92: int global_index;
! 93:
! 94: /* current stream size */
! 95: int stream_size;
! 96:
! 97: /* stream buffer size (maximum amount of bytes to hold, used
! 98: * also to measure the temporal buffer) */
! 99: int buffer_size;
! 100:
! 101: /* previous inspected stream size */
! 102: int previous_inspect;
! 103:
! 104: /* last chunk get from the stream */
! 105: char * last_chunk;
! 106:
! 107: /* last near to reference. */
! 108: char * last_near_to;
! 109:
! 110: /* last get following reference */
! 111: char * last_get_following;
! 112:
! 113: /* support variable for chunk matching */
! 114: char ** chunks;
! 115:
! 116: /* support variable for chunk matching */
! 117: int * lengths;
! 118:
! 119: /* a reference to the associated element to this stream */
! 120: axlList * elements_linked;
! 121:
! 122: /*The function to execute when the element must be destroyed. */
! 123: axlDestroyFunc element_destroy;
! 124:
! 125: /* Stream type configuration. Information source is coming
! 126: * from a file descriptor of a memory address. */
! 127: axlStreamType type;
! 128:
! 129: /* File descriptor associated with the given stream. In the
! 130: * case the stream if a STREAM_FD, this is the file descriptor
! 131: * associated. */
! 132: int fd;
! 133:
! 134: /* Temporal buffer used by the stream to handle prebuffering
! 135: * operations.
! 136: */
! 137: char * temp;
! 138: char * decode_temp;
! 139: int decode_temp_size;
! 140: int decode_temp_index;
! 141: int decode_temp_last;
! 142: int decode_temp_remain;
! 143: char * source_encoding;
! 144:
! 145: /* here is how these variables are used to decode content:
! 146: *
! 147: * [ <--- decode_temp buffer ---> ] <- decode_temp_size: total buffer size (bytes)
! 148: * ^ ^
! 149: * | |
! 150: * | +-- decode_temp_last: last valid content in the buffer (bytes)
! 151: * +-- decode_temp_index: next content to be consumed (bytes)
! 152: *
! 153: * [decode_temp_remain]: is the amount of content pending to
! 154: * be decoded. That is, starting from decode_temp_remain until
! 155: * last is the valid content still to be decoded.
! 156: *
! 157: * [source_encoding]: is defined to hold the value of the
! 158: * format for the source.
! 159: */
! 160:
! 161: /* more variables used to perform work: at get until */
! 162: char * valid_chars;
! 163: int chunk_matched;
! 164: axl_bool accept_terminator;
! 165: int result_size;
! 166: int chunk_num;
! 167:
! 168: /**
! 169: * @internal Internal variable used to notify get_until
! 170: * function that the last 0 character for stream memory
! 171: * operation done in a STREAM_MEM must be considered as a
! 172: * terminator character found.
! 173: */
! 174: axl_bool zero;
! 175:
! 176: /**
! 177: * @internal Alloc function to be used to require memory for
! 178: * chunks to be returned. This is used by Axl Stream to allow
! 179: * external modules to handle how memory is allocated while
! 180: * calling to axl_stream_get_until* API.
! 181: *
! 182: * Currently, it is used by the axl_doc module to allocate axl
! 183: * stream using a string factory.
! 184: */
! 185: axlStreamAlloc alloc;
! 186:
! 187: /**
! 188: * @internal User defined data to be passed to the alloc
! 189: * function.
! 190: */
! 191: axlPointer alloc_data;
! 192:
! 193: /**
! 194: * @internal Function used by the stream to decode content
! 195: * into utf-8. This is not used in all cases.
! 196: */
! 197: axlStreamDecode decode_f;
! 198:
! 199: /**
! 200: * @internal Reference to user defined pointer provided to the
! 201: * decode_f function.
! 202: */
! 203: axlPointer decode_f_data;
! 204:
! 205: /**
! 206: * @internal Value that allows to signal that the buffer needs
! 207: * to be expanded (no matter what shows current indexes).
! 208: */
! 209: axl_bool needs_expansion;
! 210:
! 211: /**
! 212: * @internal Reference to the content check function
! 213: * installed.
! 214: */
! 215: axlStreamContentCheck check_f;
! 216:
! 217: /**
! 218: * @internal Reference to user defined pointer to be passed to
! 219: * the content check function (check_f).
! 220: */
! 221: axlPointer check_f_data;
! 222: };
! 223:
! 224:
! 225: /**
! 226: * \defgroup axl_stream_module Axl Stream Document: Abstract stream where a xml document is expected (also provided string functions)
! 227: */
! 228:
! 229: /**
! 230: * \addtogroup axl_stream_module
! 231: * @{
! 232: */
! 233:
! 234: /**
! 235: * @internal
! 236: *
! 237: * @brief Read the next available information on the file descriptor
! 238: * placing that information into the stream.
! 239: *
! 240: * @param stream The stream where the pre-buffering operation will be
! 241: * performed.
! 242: *
! 243: * @param appended_content New content to be included at the begining
! 244: * of the stream while doing the prebuffer operation. This value could
! 245: * be null.
! 246: *
! 247: * @param appended_size The size for the appended content to be added.
! 248: *
! 249: * @return \ref axl_true if the requested padding and buffer size were
! 250: * filled or \ref axl_false if end of file was reached. In that case the
! 251: * stream size is not updated.
! 252: */
! 253: axl_bool axl_stream_prebuffer (axlStream * stream)
! 254: {
! 255: int bytes_read;
! 256: int op_result;
! 257:
! 258: /* check some environment conditions */
! 259: axl_return_val_if_fail (stream, axl_false);
! 260:
! 261: /* no prebuffering is the stream type is not a file descriptor
! 262: * source and if the socket is closed */
! 263: if (stream->type != STREAM_FD || stream->fd == -1) {
! 264: return axl_false;
! 265: } /* end if */
! 266:
! 267: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "prebuffering the stream..");
! 268:
! 269: /* displace memory only if there were data already consumed */
! 270: if (stream->stream_index > 0 && ((stream->stream_size - stream->stream_index) > 0)) {
! 271:
! 272: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " moving previous content at the begining of the buffer, current index: %d, size: %d, current status: %s",
! 273: stream->stream_index, stream->stream_size - stream->stream_index,
! 274: stream->stream + stream->stream_index);
! 275:
! 276: /* displace memory already read to be at the begining
! 277: * of the stream */
! 278: memcpy (stream->temp, stream->stream + stream->stream_index,
! 279: stream->stream_size - stream->stream_index);
! 280:
! 281: /* now copy displaced content back to the stream */
! 282: memcpy (stream->stream, stream->temp,
! 283: stream->stream_size - stream->stream_index);
! 284:
! 285: /* update the index to the positioned at the next byte
! 286: * available on the buffer */
! 287: stream->stream_size = (stream->stream_size - stream->stream_index);
! 288: } else {
! 289: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " nothing to prebuffer on the head (stream->size=%d, stream-index=%d, buffer-size=%d)",
! 290: stream->stream_size, stream->stream_index, stream->buffer_size);
! 291:
! 292: /* check here if the buffer is full of content and a call to
! 293: * prebuffer was done */
! 294: if (((stream->stream_size == stream->buffer_size) &&
! 295: (stream->stream_index == 0)) || stream->needs_expansion) {
! 296: /* looks like the caller is prebuffering
! 297: * having all buffers full of content .. */
! 298:
! 299:
! 300: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
! 301: " requested to prebuffer with buffers full of content (stream-index=%d, stream-size=%d, stream-buffer-size=%d, stream-needs-expansion=%d)",
! 302: stream->stream_index, stream->stream_size, stream->buffer_size, stream->needs_expansion);
! 303:
! 304: /* unflag expansion requested */
! 305: if (stream->needs_expansion)
! 306: stream->needs_expansion = axl_false;
! 307:
! 308: /* duplicate the buffer size */
! 309: stream->buffer_size += (stream->buffer_size);
! 310: stream->stream = realloc (stream->stream, stream->buffer_size + 1);
! 311: stream->temp = realloc (stream->temp, stream->buffer_size + 1);
! 312:
! 313: if (stream->stream == NULL) {
! 314: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
! 315: " failed to update buffer sizes (realloc operation failed)",
! 316: stream->stream_index, stream->stream_size, stream->buffer_size);
! 317: close (stream->fd);
! 318: stream->fd = -1;
! 319: return axl_false;
! 320: } /* end if */
! 321:
! 322: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
! 323: " buffer updated (stream-index=%d, stream-size=%d, stream-buffer-size=%d)",
! 324: stream->stream_index, stream->stream_size, stream->buffer_size);
! 325: } else {
! 326: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
! 327: " clearing stream-size=%d, having stream-index=%d and stream-buffer-size=%d",
! 328: stream->stream_size, stream->stream_index, stream->buffer_size);
! 329:
! 330: /* reset if the stream size is just the content consumed */
! 331: if (stream->stream_size == stream->stream_index)
! 332: stream->stream_size = 0;
! 333: }
! 334: }
! 335:
! 336: /* reset current index */
! 337: stream->stream_index = 0;
! 338:
! 339: /* check if we have decode functions defined */
! 340: if (stream->decode_f) {
! 341: while (1) {
! 342: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
! 343: "check temporal decode buffer for pending content: decode_temp_index=%d, decode_temp_last=%d, decode_temp_size=%d",
! 344: stream->decode_temp_index,
! 345: stream->decode_temp_last,
! 346: stream->decode_temp_size);
! 347:
! 348: if (stream->decode_temp_last > 0) {
! 349: /* call to decode */
! 350: if (! axl_stream_decode (stream,
! 351: /* output */
! 352: stream->stream + stream->stream_size,
! 353: /* max output size */
! 354: stream->buffer_size - stream->stream_size,
! 355: /* output decoded */
! 356: &bytes_read, &op_result, NULL)) {
! 357: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "failed to decode content at the temporal decode buffer");
! 358: return axl_false;
! 359: } /* end if */
! 360:
! 361: /* check if the decode operation
! 362: * signaled that not enough espace was
! 363: * available to decode and no output
! 364: * was decoed, int his case flag the
! 365: * stream to do a stream expansion on
! 366: * the next call */
! 367: if (op_result == 2 && bytes_read == 0)
! 368: stream->needs_expansion = axl_true;
! 369:
! 370: /* add bytes read to the size */
! 371: stream->stream_size += bytes_read;
! 372:
! 373: } /* end if */
! 374:
! 375: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
! 376: "after checking to decode, op_result=%d, bytes_read=%d, stream->buffer_size=%d, stream->stream_size=%d",
! 377: op_result, bytes_read, stream->buffer_size, stream->stream_size);
! 378:
! 379: /* stop if no enought space if left and no
! 380: * more bytes were translated */
! 381: if (op_result == 2)
! 382: break;
! 383:
! 384: /* check if there are still space to decode at the stream */
! 385: if ((stream->buffer_size - stream->stream_size) > 0) {
! 386: /* read content inside the decde temp buffer */
! 387: bytes_read = read (stream->fd, stream->decode_temp + stream->decode_temp_last,
! 388: stream->decode_temp_size - stream->decode_temp_last);
! 389:
! 390: /* update the amount of data available tat the decode temp */
! 391: if (bytes_read > 0)
! 392: stream->decode_temp_last += bytes_read;
! 393: else if (bytes_read == 0 && stream->fd > 0) {
! 394: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "end of file reached");
! 395: close (stream->fd);
! 396: stream->fd = -1;
! 397: } /* end if */
! 398: } else {
! 399: /* no more space is available at the reading buffer */
! 400: break;
! 401: } /* end if */
! 402:
! 403: /* check to terminate */
! 404: if (stream->decode_temp_index == 0 &&
! 405: stream->decode_temp_last == 0 &&
! 406: stream->fd == -1)
! 407: return axl_true;
! 408:
! 409: } /* end while */
! 410:
! 411: } else {
! 412:
! 413: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " reading on buffer index: %d, size: %d (starting from: %d, length: %d)",
! 414: stream->stream_index, stream->stream_size, stream->stream_size, stream->buffer_size - stream->stream_size);
! 415:
! 416: /* read current content */
! 417: bytes_read = read (stream->fd, stream->stream + stream->stream_size,
! 418: stream->buffer_size - stream->stream_size);
! 419:
! 420:
! 421: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " bytes read from the file, size %d", bytes_read);
! 422:
! 423: /* call to check */
! 424: if (stream->check_f) {
! 425: /* call to check */
! 426: if (! axl_stream_content_check (stream, stream->stream + stream->stream_size, bytes_read, NULL)) {
! 427: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "failed to prebuffer, content check function have failed");
! 428: return axl_false;
! 429: }
! 430: } /* end if */
! 431:
! 432: /* check for end of file reached */
! 433: if (bytes_read == 0) {
! 434: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "end of file reached");
! 435: close (stream->fd);
! 436: stream->fd = -1;
! 437: return axl_false;
! 438: }
! 439:
! 440: /* set the new size, that is the padding, the content already
! 441: * read, and the bytes already read */
! 442: stream->stream_size += bytes_read;
! 443: } /* end if */
! 444:
! 445: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " (before read) current buffer size: %d",
! 446: stream->stream_size);
! 447:
! 448: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " prebuffered data: stream size: %d, new content: %s",
! 449: bytes_read, stream->stream + stream->stream_index);
! 450:
! 451: return axl_true;
! 452: }
! 453:
! 454: /**
! 455: * @brief Creates a new byte stream using as data the string pointer
! 456: * and the size.
! 457: *
! 458: * The \ref axlStream object is an abstraction that allows to
! 459: * interface with a memory chunk, a file descriptor (or a socket) or a
! 460: * direct path, with a contenience API that allows inspecting and
! 461: * accepting the streamed content.
! 462: *
! 463: *
! 464: * Here is an example on how to create an stream from a memory chunk:
! 465: * \code
! 466: * axlStream * stream;
! 467: * axlError * error;
! 468: *
! 469: * // create the stream and check result
! 470: * stream = axl_stream_new (source_content, size_content, -1, NULL, &error);
! 471: * if (stream == NULL) {
! 472: * printf ("An error was found: %s\n", axl_error_get (error));
! 473: * axl_error_free (error);
! 474: * return;
! 475: * }
! 476: *
! 477: * // stream created
! 478: * \endcode
! 479: *
! 480: * In the case an stream is required to parse a file descriptor
! 481: * (including a socket):
! 482: *
! 483: * \code
! 484: * stream = axl_stream_new (NULL, -1, NULL, fd, &error);
! 485: * \endcode
! 486: *
! 487: * You can also provide a file path to let the axl stream module to
! 488: * open the file and buffer it as it requires to consume characters:
! 489: *
! 490: * \code
! 491: * stream = axl_stream_new (NULL, -1, "c:/myFiles/document.xml", -1, &error);
! 492: * \endcode
! 493: *
! 494: *
! 495: * Once initialized the \ref axlStream object, you can use the
! 496: * following function to check and consume characters:
! 497: *
! 498: * - \ref axl_stream_inspect
! 499: * - \ref axl_stream_peek
! 500: * - \ref axl_stream_accept
! 501: *
! 502: * There alternatives APIs that allows to get the content until some
! 503: * patter is found (or a set of patterns):
! 504: *
! 505: * - \ref axl_stream_get_until
! 506: *
! 507: *
! 508: * @param stream_source A pointer to the memory where the data to be
! 509: * streamed is located.
! 510: *
! 511: * @param stream_size How many bytes are available to perform
! 512: * streaming. You can pass a -1 value to allow the function to
! 513: * calculate the stream source size (stream_source param).
! 514: *
! 515: * @param file_path Optional parameter to allow reading the stream
! 516: * from a file using the open API.
! 517: *
! 518: * @param fd_handler Optional parameter to allow reading the stream
! 519: * from a file descriptor, that could be a file, a socket, etc..
! 520: *
! 521: * @param error Optional \ref axlError reference where errors will be reported.
! 522: *
! 523: * @return A newly allocated stream instance that should be
! 524: * deallocated by using \ref axl_stream_free. The function could
! 525: * return a NULL value is received a NULL stream or a non positive
! 526: * stream size.
! 527: */
! 528: axlStream * axl_stream_new (const char * stream_source, int stream_size,
! 529: const char * file_path, int fd_handler,
! 530: axlError ** error)
! 531: {
! 532: axlStream * stream;
! 533: int fd;
! 534:
! 535: /* perform some environmental checkings */
! 536: if (file_path != NULL || (fd_handler > 0)) {
! 537: if (fd_handler < 0) {
! 538: /* a file handle */
! 539: if ((fd = open (file_path, O_RDONLY)) < 0) {
! 540: axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "unable to open file a the location provided: %s, check location and permissions.", file_path);
! 541:
! 542: axl_error_new (-1, "unable to read file provided", NULL, error);
! 543: return NULL;
! 544: }
! 545: }else
! 546: fd = fd_handler;
! 547:
! 548: /* create the stream holder */
! 549: stream = axl_new (axlStream, 1);
! 550: stream->buffer_size = STREAM_BUFFER_SIZE;
! 551: stream->type = STREAM_FD;
! 552: stream->fd = fd;
! 553:
! 554: /* allocate 4k to perform streaming */
! 555: stream->stream = axl_new (char, stream->buffer_size + 1);
! 556: stream->temp = axl_new (char, stream->buffer_size + 1);
! 557:
! 558: /* prebuffer */
! 559: axl_stream_prebuffer (stream);
! 560: }else {
! 561: /* check chunk received */
! 562: if (stream_source == NULL) {
! 563: axl_error_new (-1, "Requested to open a stream without providing an memory chunk, file descriptor or a file path", NULL, error);
! 564: return NULL;
! 565: }
! 566:
! 567: /* check memory chunk size */
! 568: if (stream_size == -1)
! 569: stream_size = strlen (stream_source);
! 570:
! 571: /* create the stream holder */
! 572: stream = axl_new (axlStream, 1);
! 573: stream->buffer_size = stream_size;
! 574: stream->type = STREAM_MEM;
! 575:
! 576: /* copy source */
! 577: stream->stream = axl_new (char, stream_size + 1);
! 578: memcpy (stream->stream, stream_source, stream_size);
! 579: /* nullify the last entry to avoid problems with
! 580: printing functions and other APIs relaying on
! 581: finding \0 at the end of the string */
! 582: stream->stream[stream_size] = 0;
! 583:
! 584: /* set initial indicators */
! 585: stream->stream_size = stream_size;
! 586: }
! 587:
! 588: /* initilize common stream part */
! 589: stream->chunks = axl_new (char *, MAX_INSPECTED_CHUNKS + 1);
! 590: stream->lengths = axl_new (int, MAX_INSPECTED_CHUNKS + 1);
! 591:
! 592: /* return newly created stream */
! 593: return stream;
! 594: }
! 595:
! 596: /**
! 597: * @internal
! 598: *
! 599: * Internal implementation to support axl_stream_inspect and
! 600: * axl_stream_peek.
! 601: *
! 602: * @param stream The stream where the inspect operation will be
! 603: * performed.
! 604: *
! 605: * @param chunk The chunk to be used while performing the inspect
! 606: * operation.
! 607: *
! 608: * @param inspected_size The size for the chunk to be checked or -1 if
! 609: * a chunk size calculation is required.
! 610: *
! 611: * @param alsoAccept axl_true to make the function to accept stream
! 612: * that is properly inspected, axl_false if not.
! 613: *
! 614: * @return See \ref axl_stream_inspect.
! 615: */
! 616: #define axl_stream_common_inspect(i,stream,chunk,inspected_size,alsoAccept)\
! 617: if (inspected_size == -1)\
! 618: inspected_size = strlen (chunk);\
! 619: if (axl_stream_fall_outside (stream, inspected_size))\
! 620: return -1;\
! 621: i = 0;\
! 622: while (chunk [i] != 0 && (stream->stream + stream->stream_index) [i] != 0) {\
! 623: if (chunk [i] != (stream->stream + stream->stream_index) [i])\
! 624: return 0;\
! 625: i++;\
! 626: if (i == inspected_size) {\
! 627: stream->previous_inspect = inspected_size;\
! 628: if (alsoAccept) {\
! 629: axl_stream_accept (stream);\
! 630: }\
! 631: return 1;\
! 632: }\
! 633: }\
! 634: return 0
! 635:
! 636:
! 637: /**
! 638: * @brief Allows to perform an inspection of the given chunk on the
! 639: * given stream.
! 640: *
! 641: * The <i>chunk</i> will be checked to apper at the begining of the
! 642: * stream. This means that, having the current state of the stream,
! 643: * the chunk is checked to be found at the very begining of the
! 644: * stream.
! 645: *
! 646: * If the function succeed, an implict call to \ref axl_stream_accept
! 647: * is done. In the case you are only checking but no stream acceptance
! 648: * is required, use instead: \ref axl_stream_peek.
! 649: *
! 650: * @param stream The \ref axlStream where the operation will be
! 651: * performed.
! 652: *
! 653: * @param chunk The chunk that is expected to be found at the begin of the stream.
! 654: *
! 655: * @param inspected_size The size of the chunk provided to be inspected.
! 656: *
! 657: * @return The function returns the following values according to the result:
! 658: *
! 659: * - <b>0</b> if the chunk wasn't found inside the stream but no error was
! 660: * found.
! 661: *
! 662: * - <b>1</b> if the chunk is found inside the given stream.
! 663: *
! 664: * - <b>-1</b> means that no more stream is left to satify the operation.
! 665: *
! 666: * - <b>-2</b> means that the parameters received are wrong either
! 667: * because stream is a NULL reference or because chunk is the same.
! 668: */
! 669: int axl_stream_inspect (axlStream * stream, const char * chunk, int inspected_size)
! 670: {
! 671: int iterator;
! 672:
! 673: /* call to common implementation */
! 674: axl_stream_common_inspect (iterator, stream, chunk, inspected_size, axl_true);
! 675: }
! 676:
! 677: /**
! 678: * @brief Allows to check the provide char code at the given \ref
! 679: * axlStream, using optionally an offset to perform the check.
! 680: *
! 681: * @param stream The stream where the check operation will be
! 682: * implemented.
! 683: *
! 684: * @param value The value to check.
! 685: *
! 686: * @param offset The stream offset to apply to the check. Use 0 to not
! 687: * apply offset.
! 688: *
! 689: * @return \ref axl_true if the provided value is found at the current
! 690: * stream index (taking into consideration offset).
! 691: */
! 692: axl_bool axl_stream_inspect_code (axlStream * stream, char value, int offset)
! 693: {
! 694: axl_return_val_if_fail (stream, axl_false);
! 695:
! 696: /* check the value */
! 697: return stream->stream [stream->stream_index + offset] == value;
! 698: }
! 699:
! 700: /**
! 701: * @brief Allows to perform a stream inspection without automatically
! 702: * accept content properly inspected.
! 703: *
! 704: * @param stream The stream where the peek operation will be
! 705: * performed.
! 706: *
! 707: * @param chunk The chunk to lookup.
! 708: *
! 709: * @param inspected_size The size of the chunk provided to be
! 710: * inspected.
! 711: *
! 712: * @return See \ref axl_stream_inspect.
! 713: */
! 714: int axl_stream_peek (axlStream * stream, const char * chunk, int inspected_size)
! 715: {
! 716: int iterator;
! 717:
! 718: /* call to common implementation */
! 719: axl_stream_common_inspect (iterator, stream, chunk, inspected_size, axl_false);
! 720: }
! 721:
! 722: /**
! 723: * @brief Allows to perform several, not excluyen inspect operations,
! 724: * over the given stream.
! 725: *
! 726: * Here is an example:
! 727: * \code
! 728: * if (axl_stream_inspect_several (stream, // the stream
! 729: * 2, // two chunks to recognize
! 730: * "or", 2, // first chunk and its length
! 731: * "||", 2) > 0) { // second chunk and its length
! 732: * // chunk matched!!
! 733: * }
! 734: * \endcode
! 735: * @param stream The stream where the operation will be performed.
! 736: *
! 737: * @param chunk_num The chunk number to inspect.
! 738: *
! 739: * @return The function returns the following values:
! 740: *
! 741: * - <b>0</b>: if no chunk is found inside the given stream, according to the
! 742: * provided chunks.
! 743: *
! 744: * - <b>N</b>: is returned to denote that the Nth chunk was found.
! 745: *
! 746: * - <b>-1</b>: is returned if no more stream is left to satisfy the operation.
! 747: *
! 748: * - <b>-2</b>: means that the parameters received are wrong either because
! 749: * stream is NULL or any other parameter.
! 750: */
! 751: int axl_stream_inspect_several (axlStream * stream, int chunk_num, ...)
! 752: {
! 753: va_list args;
! 754: int iterator = 0;
! 755: char * chunk = NULL;
! 756: int length = 0;
! 757: int last_value = 0;
! 758:
! 759: axl_return_val_if_fail (stream, -1);
! 760: axl_return_val_if_fail (chunk_num > 0, -1);
! 761:
! 762: va_start (args, chunk_num);
! 763:
! 764: /* check each chunk */
! 765: while (iterator < chunk_num) {
! 766:
! 767: /* get the next chunk */
! 768: chunk = va_arg (args, char *);
! 769: length = va_arg (args, int);
! 770:
! 771: if (length == -1)
! 772: length = strlen (chunk);
! 773:
! 774: /* check the chunk read */
! 775: switch (axl_stream_inspect (stream, chunk, length)) {
! 776: case -2:
! 777: /* wrong parameter received */
! 778: last_value = -2;
! 779: break;
! 780: case -1:
! 781: /* there is no more stream left */
! 782: last_value = -1;
! 783: break;
! 784: case 0:
! 785: /* the chunk wasn't found, break and
! 786: * continue. */
! 787: break;
! 788: default:
! 789: /* the chunk was found */
! 790: va_end (args);
! 791: return (iterator + 1);
! 792: }
! 793:
! 794: /* update the iterator */
! 795: iterator ++;
! 796: }
! 797:
! 798: /* close va arg */
! 799: va_end (args);
! 800:
! 801: /* return that no chunk was found */
! 802: return last_value;
! 803: }
! 804:
! 805: /**
! 806: * @brief Accept previous inspected chunk to be consumed and moves
! 807: * current stream current the size equal to the chunk inspected.
! 808: *
! 809: * @param stream The stream where the byte inspected size will be
! 810: * accepted.
! 811: */
! 812: void axl_stream_accept (axlStream * stream)
! 813: {
! 814: axl_return_if_fail (stream);
! 815:
! 816: /* simple memory chunk parsing */
! 817: stream->stream_index += stream->previous_inspect;
! 818: stream->global_index += stream->previous_inspect;
! 819:
! 820: stream->previous_inspect = 0;
! 821: if (stream->last_chunk != NULL)
! 822: axl_free (stream->last_chunk);
! 823: stream->last_chunk = NULL;
! 824:
! 825: return;
! 826: }
! 827:
! 828: /**
! 829: * @brief Push new content at the begin of the stream.
! 830: *
! 831: * @param stream The stream that will be updated with new content.
! 832: *
! 833: * @param content The content to be added.
! 834: *
! 835: * @param size The size of the content to be added.
! 836: */
! 837: void axl_stream_push (axlStream * stream, const char * content, int size)
! 838: {
! 839: axl_return_if_fail (stream && content);
! 840:
! 841: /* place the content at the begin of the stream */
! 842: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "calling to push the stream..");
! 843:
! 844: /* check if the current stream buffer could hold the pushed
! 845: * content plus the content that is already placed */
! 846: if (stream->stream_size < (stream->stream_size - stream->stream_index + size)) {
! 847: /* seems we can't hold the content at this moment, so, update the stream size */
! 848: stream->buffer_size = stream->stream_size - stream->stream_index + size;
! 849:
! 850: /* alloc a new temporal buffer */
! 851: axl_free (stream->temp);
! 852: stream->temp = axl_new (char, stream->buffer_size + 1);
! 853: memcpy (stream->temp, content, size);
! 854:
! 855: /* displace memory already read to be at the begining
! 856: * of the stream */
! 857: memcpy (stream->temp + size, stream->stream + stream->stream_index,
! 858: stream->stream_size - stream->stream_index);
! 859:
! 860: /* now realloc the buffer */
! 861: axl_free (stream->stream);
! 862: stream->stream = axl_new (char, stream->buffer_size + 1);
! 863:
! 864: /* now copy displaced content back to the stream */
! 865: memcpy (stream->stream, stream->temp,
! 866: (stream->stream_size - stream->stream_index) + size);
! 867: } else {
! 868:
! 869: /* check for the temporal buffer to be created */
! 870: if (stream->temp == NULL)
! 871: stream->temp = axl_new (char, stream->buffer_size + 1);
! 872:
! 873: /* copy the content */
! 874: memcpy (stream->temp, content, size);
! 875:
! 876: /* displace memory already read to be at the begining
! 877: * of the stream */
! 878: memcpy (stream->temp + size, stream->stream + stream->stream_index,
! 879: stream->stream_size - stream->stream_index);
! 880:
! 881: /* now copy displaced content back to the stream */
! 882: memcpy (stream->stream, stream->temp,
! 883: (stream->stream_size - stream->stream_index) + size);
! 884:
! 885: } /* end if */
! 886:
! 887: /* update the index to the positioned at the next byte
! 888: * available on the buffer */
! 889: stream->stream_size = (stream->stream_size - stream->stream_index) + size;
! 890:
! 891: /* reset the index */
! 892: stream->stream_index = 0;
! 893:
! 894: /* clean previous state */
! 895: axl_stream_accept (stream);
! 896:
! 897: return;
! 898: }
! 899:
! 900: /**
! 901: * @brief Allows to configure current index to be accepted by the
! 902: * stream.
! 903: *
! 904: * @param stream The stream where the operation will be performed.
! 905: *
! 906: * @param index Count to move internal stream index.
! 907: *
! 908: * NOTE: the function reset current internal state (by doing an
! 909: * implicit call to \ref axl_stream_accept).
! 910: */
! 911: void axl_stream_move (axlStream * stream, int index)
! 912: {
! 913: axl_return_if_fail (stream);
! 914:
! 915: axl_stream_accept (stream);
! 916: stream->stream_index = index;
! 917:
! 918: return;
! 919: }
! 920:
! 921: /**
! 922: * @brief Makes the current index to be moved the amount of bytes
! 923: * signaled by the parameter bytes.
! 924: *
! 925: * @param stream The stream to update.
! 926: * @param bytes The number of bytes to move the index.
! 927: */
! 928: void axl_stream_step (axlStream * stream, int bytes)
! 929: {
! 930: axl_return_if_fail (stream);
! 931: axl_return_if_fail (bytes >= 0);
! 932:
! 933: axl_stream_accept (stream);
! 934: stream->stream_index += bytes;
! 935:
! 936: return;
! 937: }
! 938:
! 939: /**
! 940: * @brief Returns the next chunk available on the stream.
! 941: *
! 942: * This function allows to get next available chunk, validating it
! 943: * with provided valid_chars variable, until the chunk provided are
! 944: * found.
! 945: *
! 946: * Currently, valid_chars is not used, so, the chunk returned is not
! 947: * validated against the value provided.
! 948: *
! 949: * As an example if it is required to get the encoding content, you
! 950: * could do the next call:
! 951: *
! 952: * \code
! 953: * // reference to the allocated result
! 954: * char * result;
! 955: *
! 956: * // chunk matched variable
! 957: * int chunk_matched;
! 958: *
! 959: * // get the next chunk until a " or ' is found
! 960: * result = axl_stream_get_until (stream, NULL, &chunk_matched, AXL_TRUE, 2, "\"", "'");
! 961: * \endcode
! 962: *
! 963: * Value returned from this function mustn't be deallocated. However,
! 964: * because the value returned is dinamically allocated by the
! 965: * function, you can avoid doing a double allocation operation by
! 966: * nullifying the internal reference to the result returned, making
! 967: * the caller the only owner of the reference returned. To do this
! 968: * use: \ref axl_stream_nullify with \ref LAST_CHUNK.
! 969: *
! 970: *
! 971: * @param stream The stream were the chunk will be extracted.
! 972: *
! 973: * @param valid_chars The valid set of characters, to validate content
! 974: * to be returned. Currently this is not implemented, so, you can
! 975: * provide a NULL value.
! 976: *
! 977: * @param chunk_matched An optional pointer to an integer to notify
! 978: * the chunk matched by the function. Chunk matching notification
! 979: * starts from 0 up to number of chunks to match - 1. If the end of
! 980: * the stream is reached, -2 is returned.
! 981: *
! 982: * @param accept_terminator While calling to this function, the
! 983: * terminator detected to stop the operation could also be accepted by
! 984: * the stream, making it not necessary to accept the terminator once
! 985: * the function have ended. However, this could be a problem while
! 986: * performing peeking code. You can provide a AXL_FALSE value to make the
! 987: * function to not accept the terminator found as to be consumed.
! 988: *
! 989: * @param chunk_num The number of chunks to be checked as a valid terminators.
! 990: *
! 991: * @return The chunk recognizied, not including the terminator that
! 992: * have made this operation to stop.
! 993: */
! 994: char * axl_stream_get_until (axlStream * stream,
! 995: char * valid_chars,
! 996: int * chunk_matched,
! 997: axl_bool accept_terminator,
! 998: int chunk_num, ...)
! 999: {
! 1000: char * result;
! 1001: va_list args;
! 1002:
! 1003: /* open the standard argument */
! 1004: va_start (args, chunk_num);
! 1005:
! 1006: /* call to get next chunk separated by the provided values */
! 1007: result = axl_stream_get_untilv (stream, valid_chars, chunk_matched, accept_terminator, NULL, chunk_num, args);
! 1008:
! 1009: /* close the standard argument */
! 1010: va_end (args);
! 1011:
! 1012: /* return value read */
! 1013: return result;
! 1014: }
! 1015:
! 1016: /**
! 1017: * @brief Works the same way like axl_strteam_get_until but wihtout
! 1018: * allocating the memory returned, and filling the size for the chunk
! 1019: * returned in result_size reference.
! 1020: *
! 1021: * @param stream The stream where the operation will be performed.
! 1022: *
! 1023: * @param valid_chars The valid chars reference to match (currently
! 1024: * not implemented).
! 1025: *
! 1026: * @param chunk_matched The chunk matched reference
! 1027: *
! 1028: * @param accept_terminator Configure if the terminator should be
! 1029: * accepted or not.
! 1030: *
! 1031: * @param result_size The variable where the result size will be
! 1032: * returned. This variable is not optional. It must be configured to
! 1033: * hold the size of the content returned. If you provided a NULL
! 1034: * reference to this value then the function will fail.
! 1035: *
! 1036: * @param chunk_num The number of chunks to match.
! 1037: *
! 1038: * @return A reference to the internal stream copy. The reference
! 1039: * returned must not be deallocated.
! 1040: *
! 1041: * NOTE: This function have a particular function that could produce
! 1042: * not desired results. Because the stream returns a reference to the
! 1043: * current allocated stream, if nullifies the last position (\\0) to
! 1044: * avoid memory problems with printf APIs and any other code that
! 1045: * relay on the fact that C strings are NULL terminated. If the
! 1046: * content immediately following to the string returned is meaningful,
! 1047: * then you can't use this function. Example:
! 1048: *
! 1049: * \code
! 1050: * stream: CONTENTCONTENT2
! 1051: * ^
! 1052: * |
! 1053: * +--- stream index
! 1054: *
! 1055: * calling to axl_stream_get_until_ref (stream, NULL, NULL, axl_false,
! 1056: * &size, 1, "CONTENT");
! 1057: *
! 1058: * while cause stream: CONTENT\0ONTENT2
! 1059: * ^
! 1060: * |
! 1061: * +--- stream index
! 1062: *
! 1063: * and the function returning "CONTENT". See the fact that the
! 1064: * next "C" from the word CONTENT2 is nullified.
! 1065: *
! 1066: * \endcode
! 1067: *
! 1068: * An indication that this function is not what you want is that you
! 1069: * are not accepting the terminator (accept_terminator=axl_false).
! 1070: */
! 1071: char * axl_stream_get_until_ref (axlStream * stream,
! 1072: char * valid_chars,
! 1073: int * chunk_matched,
! 1074: axl_bool accept_terminator,
! 1075: int * result_size,
! 1076: int chunk_num, ...)
! 1077: {
! 1078: char * result;
! 1079: va_list args;
! 1080:
! 1081: axl_return_val_if_fail (result_size, NULL);
! 1082:
! 1083: /* open the standard argument */
! 1084: va_start (args, chunk_num);
! 1085:
! 1086: /* call to get next chunk separated by the provided values */
! 1087: result = axl_stream_get_untilv (stream, valid_chars, chunk_matched, accept_terminator, result_size, chunk_num, args);
! 1088:
! 1089: /* close the standard argument */
! 1090: va_end (args);
! 1091:
! 1092: /* return value read */
! 1093: return result;
! 1094: }
! 1095:
! 1096: /**
! 1097: * @brief Allows to get the next string until the separators provided
! 1098: * are found or the end of the stream memory is reached.
! 1099: *
! 1100: * \ref axlStream type was designed to support parsing xml
! 1101: * documents. This documents have elements that allows to now where
! 1102: * the input has finished. Howerver, \ref axlStream abstraction has
! 1103: * showed to be powerful enough to be usable to parse other kinds of
! 1104: * elements that don't have lexical terminators to let the user to
! 1105: * provide that chunk to be matched.
! 1106: *
! 1107: * In those cases, this function allows to perform the same function
! 1108: * as \ref axl_stream_get_until but also checking, and using, as
! 1109: * terminator the end of the stream.
! 1110: *
! 1111: * This allows to parse expressions like:
! 1112: * \code
! 1113: * int chunk_matched;
! 1114: * axlStream * stream;
! 1115: * char * string;
! 1116: *
! 1117: * // create the stream
! 1118: * stream = axl_stream_new ("array.value", -1, NULL, -1, &error);
! 1119: *
! 1120: * // parse first array identifier
! 1121: * string = axl_stream_get_until_zero (stream, NULL, &chunk_matched,
! 1122: * axl_false, 2, "[", ".", NULL);
! 1123: *
! 1124: * // received "array"
! 1125: *
! 1126: * // parse again
! 1127: * string = axl_stream_get_until_zero (stream, NULL, &chunk_matched,
! 1128: * axl_false, 2, "[", ".", NULL);
! 1129: *
! 1130: * // received "value" and chunk_matched == (-2)
! 1131: * \endcode
! 1132: *
! 1133: *
! 1134: * @param stream The stream were the chunk will be extracted.
! 1135: *
! 1136: * @param valid_chars The valid set of characters, to validate content
! 1137: * to be returned. Currently this is not implemented, so, you can
! 1138: * provide a NULL value.
! 1139: *
! 1140: * @param chunk_matched An optional pointer to an integer to notify
! 1141: * the chunk matched by the function. Chunk matching notification
! 1142: * starts from 0 up to number of chunks to match - 1. If the end of
! 1143: * the stream is reached while searching for the content to match,
! 1144: * chunk_matched is configured to -2.
! 1145: *
! 1146: * @param accept_terminator While calling to this function, the
! 1147: * terminator detected to stop the operation could also be accepted by
! 1148: * the stream, making it not necessary to accept the terminator once
! 1149: * the function have ended. However, this could be a problem while
! 1150: * performing peeking code. You can provide a AXL_FALSE value to make the
! 1151: * function to not accept the terminator found as to be consumed.
! 1152: *
! 1153: * @param chunk_num The number of chunks to be checked as a valid terminators.
! 1154: *
! 1155: * @return The chunk recognizied, not including the terminator that
! 1156: * have made this operation to stop. Rembember to check the
! 1157: * chunk_matched variable to be equal to -2. This will mean that the
! 1158: * string returned doesn't match any terminator provided because end
! 1159: * of the stream was reached while looking for them.
! 1160: */
! 1161: char * axl_stream_get_until_zero (axlStream * stream,
! 1162: char * valid_chars,
! 1163: int * chunk_matched,
! 1164: axl_bool accept_terminator,
! 1165: int chunk_num, ...)
! 1166: {
! 1167: char * result;
! 1168: va_list args;
! 1169:
! 1170: /* open the standard argument */
! 1171: va_start (args, chunk_num);
! 1172:
! 1173: /* call to get next chunk separated by the provided values */
! 1174: stream->zero = axl_true;
! 1175: result = axl_stream_get_untilv (stream, valid_chars, chunk_matched, accept_terminator, NULL, chunk_num, args);
! 1176: stream->zero = axl_false;
! 1177:
! 1178: /* close the standard argument */
! 1179: va_end (args);
! 1180:
! 1181: /* return value read */
! 1182: return result;
! 1183: }
! 1184:
! 1185: char * axl_stream_get_until_ref_zero (axlStream * stream,
! 1186: char * valid_chars,
! 1187: int * chunk_matched,
! 1188: axl_bool accept_terminator,
! 1189: int * result_size,
! 1190: int chunk_num, ...)
! 1191: {
! 1192: char * result;
! 1193: va_list args;
! 1194:
! 1195: /* open the standard argument */
! 1196: va_start (args, chunk_num);
! 1197:
! 1198: /* call to get next chunk separated by the provided values */
! 1199: stream->zero = axl_true;
! 1200: result = axl_stream_get_untilv (stream, valid_chars, chunk_matched, accept_terminator, result_size, chunk_num, args);
! 1201: stream->zero = axl_false;
! 1202:
! 1203: /* close the standard argument */
! 1204: va_end (args);
! 1205:
! 1206: /* return value read */
! 1207: return result;
! 1208: }
! 1209:
! 1210: /**
! 1211: * @brief Allows to configure the handler to be executed to alloc
! 1212: * memory for the axl_stream_get_until* API.
! 1213: *
! 1214: * @param stream The stream to configure with the alloc function.
! 1215: *
! 1216: * @param handler The handler to be called when the streams requires
! 1217: * to alloc memory.
! 1218: *
! 1219: * @param data User defined pointer to be called to the allocator
! 1220: * function defined by the <b>handler</b> parameter.
! 1221: */
! 1222: void axl_stream_set_buffer_alloc (axlStream * stream,
! 1223: axlStreamAlloc handler,
! 1224: axlPointer data)
! 1225: {
! 1226: axl_return_if_fail (stream);
! 1227:
! 1228: /* just configure the alloc handler */
! 1229: stream->alloc = handler;
! 1230: stream->alloc_data = data;
! 1231:
! 1232: return;
! 1233: }
! 1234:
! 1235: /**
! 1236: * @brief Allows to nullify the internal reference of the stream,
! 1237: * making that reference to be not deallocated once the stream is
! 1238: * moving.
! 1239: *
! 1240: * This is mainly used to reduce the malloc/free round trip while
! 1241: * using the stream abstraction, making the stream received from the
! 1242: * memory chunk to be allocated only once, avoiding the double
! 1243: * allocate-free cycle.
! 1244: *
! 1245: * @param stream The \ref axlStream where the operation will be
! 1246: * performed.
! 1247: *
! 1248: * @param item The item to nullify.
! 1249: */
! 1250: void axl_stream_nullify (axlStream * stream,
! 1251: NullifyItem item)
! 1252: {
! 1253: /* do not operate if a null stream reference is received */
! 1254: axl_return_if_fail (stream);
! 1255:
! 1256: switch (item) {
! 1257: case LAST_CHUNK:
! 1258: stream->last_chunk = NULL;
! 1259: break;
! 1260: case LAST_NEAR_TO:
! 1261: stream->last_near_to = NULL;
! 1262: break;
! 1263: case LAST_GET_FOLLOWING:
! 1264: stream->last_get_following = NULL;
! 1265: break;
! 1266: }
! 1267:
! 1268: /* nothing more to do here */
! 1269: return;
! 1270: }
! 1271:
! 1272: /**
! 1273: * @internal
! 1274: *
! 1275: * Wide implementation for axl stream get until (which checks for
! 1276: * every index chunks provided instead of checking the first one until
! 1277: * a prebuffer operation is required).
! 1278: */
! 1279: char * __axl_stream_get_untilv_wide (axlStream * stream, va_list args)
! 1280: {
! 1281:
! 1282: int iterator = 0;
! 1283: int index = 0;
! 1284: int _index = 0;
! 1285: int length = 0;
! 1286: int max_length = 0;
! 1287: axl_bool matched;
! 1288: char * string = NULL;
! 1289: axl_bool match_empty = axl_false;
! 1290: int empty_index = 0;
! 1291:
! 1292: /* get how many bytes remains to be read */
! 1293: int remains;
! 1294:
! 1295: /* set current matched value */
! 1296: stream->chunk_matched = -1;
! 1297:
! 1298: /* iterate over the chunk list */
! 1299: while (iterator < stream->chunk_num) {
! 1300:
! 1301: /* get the chunk */
! 1302: stream->chunks [iterator] = va_arg (args, char *);
! 1303:
! 1304: /* check if we have to match the emtpy string, and
! 1305: * don't install the value to be matched */
! 1306: if (axl_cmp (stream->chunks[iterator], " ")) {
! 1307: match_empty = axl_true;
! 1308:
! 1309: /* reset the length size to detect an emtpy
! 1310: * string */
! 1311: stream->lengths [iterator] = 0;
! 1312: empty_index = iterator;
! 1313: }else {
! 1314: /* get the size */
! 1315: stream->lengths [iterator] = strlen (stream->chunks [iterator]);
! 1316: }
! 1317:
! 1318: /* get current length */
! 1319: if (stream->lengths [iterator] > max_length)
! 1320: max_length = stream->lengths [iterator];
! 1321:
! 1322: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found matching chunk: '%s' length: %d",
! 1323: stream->chunks[iterator], stream->lengths [iterator]);
! 1324:
! 1325: /* update index */
! 1326: iterator ++;
! 1327: }
! 1328:
! 1329: /* calculate how many bytes ara available */
! 1330: remains = stream->stream_size - stream->stream_index;
! 1331:
! 1332: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "remaining data to be inspected: %d bytes (stream->stream-size=%d, stream->stream-index=%d)",
! 1333: remains, stream->stream_size, stream->stream_index);
! 1334:
! 1335: /* now we have chunks to lookup, stream until get the stream
! 1336: * limited by the chunks received. */
! 1337: do {
! 1338:
! 1339: /* decrease remain bytes to be read until perform a
! 1340: * prebuffer operation */
! 1341: remains--;
! 1342:
! 1343: /* only prebuffer for this type of stream */
! 1344: if (stream->type == STREAM_FD) {
! 1345:
! 1346: /* check if the index is falling out side the buffer boundaries */
! 1347: if (remains < 0) {
! 1348:
! 1349: if (! axl_stream_prebuffer (stream)) {
! 1350: /* check if a call to zero was found */
! 1351: if (stream->zero) {
! 1352: /* flag that chunk matched
! 1353: * will be -2 */
! 1354: stream->chunk_matched = -2;
! 1355: goto matched_return_result;
! 1356: } /* end if */
! 1357: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "failed while prebuffer (stream->type = %d)", stream->type);
! 1358:
! 1359: return NULL;
! 1360: }
! 1361:
! 1362: /* update remains value but this time removing
! 1363: * one unit (the unit to be consumed at the
! 1364: * next sentence) */
! 1365: remains = stream->stream_size - index - 1;
! 1366:
! 1367: /* work around: because the index is updated
! 1368: * at the end of the loop, it is required to
! 1369: * decrease the index in one unit in the case
! 1370: * a prebuffer operation happens */
! 1371: if (index > 0) {
! 1372: index--;
! 1373: }
! 1374:
! 1375: } /* end if */
! 1376: }
! 1377:
! 1378: /* check we don't get out side the memory */
! 1379: if (stream->type == STREAM_MEM) {
! 1380: if (remains < 0) {
! 1381:
! 1382: /* check for zero stream ended
! 1383: * support */
! 1384: if (stream->zero) {
! 1385: /* flag that chunk matched
! 1386: * will be -2 */
! 1387: stream->chunk_matched = -2;
! 1388: goto matched_return_result;
! 1389: }
! 1390:
! 1391: /* seems there is no more room to
! 1392: * read */
! 1393: return NULL;
! 1394: } /* end if */
! 1395: } /* end if */
! 1396:
! 1397:
! 1398: /* compare chunks received for each index increased
! 1399: * one step */
! 1400: init_get_until:
! 1401: _index = stream->stream_index + index;
! 1402: matched = axl_false;
! 1403: iterator = 0;
! 1404:
! 1405: /* before iterating, check if we have to match the
! 1406: * empty string */
! 1407: if (match_empty) {
! 1408: /* check for a white space item */
! 1409: if ((stream->stream[_index] == ' ') ||
! 1410: (stream->stream[_index] == '\n') ||
! 1411: (stream->stream[_index] == '\t') ||
! 1412: (stream->stream[_index] == '\r')) {
! 1413:
! 1414: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "matched by white space value matched_chunk=%d",
! 1415: iterator);
! 1416:
! 1417: /* string matched */
! 1418: length = 1;
! 1419: matched = axl_true;
! 1420: iterator = empty_index;
! 1421: }
! 1422: } /* end if */
! 1423:
! 1424: /* the empty string wasn't matched, now check for the
! 1425: * rest of chunks */
! 1426: while ((! matched) && (iterator < stream->chunk_num)) {
! 1427:
! 1428: /* get current length for the chunk to check */
! 1429: length = stream->lengths [iterator];
! 1430:
! 1431: /* check the length returned */
! 1432: matched = axl_false;
! 1433: if (length > 0 && ((_index + length) <= stream->stream_size)) {
! 1434:
! 1435: /* try to figure out if the next
! 1436: * string match */
! 1437: if ((stream->chunks [iterator][0] ==
! 1438: stream->stream [_index])) {
! 1439:
! 1440: if ((length == 1) ||
! 1441: (axl_memcmp (stream->chunks [iterator] + 1, stream->stream + _index + 1, length -1))) {
! 1442: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "matched as normal situation.. at index=%d (stream size=%d)",
! 1443: stream->stream_index + _index + 1, stream->stream_size);
! 1444: /* flag as matched */
! 1445: matched = axl_true;
! 1446: } /* end if */
! 1447: } /* end if */
! 1448: } /* end if */
! 1449:
! 1450: /* check if we have found the chunk we were looking */
! 1451: if (! matched) {
! 1452: /* update iterator */
! 1453: iterator ++;
! 1454: }
! 1455: } /* end while */
! 1456:
! 1457: /* check that the function have found a chunk */
! 1458: if (matched) {
! 1459: /* check for matching a more specific
! 1460: * terminal than a general */
! 1461: if ((length < max_length) &&
! 1462: ((_index + length) == stream->stream_size)) {
! 1463: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "detected possible false positive");
! 1464:
! 1465: /* do a prebuffer operation,
! 1466: * and if success, try to
! 1467: * check again all chunks at
! 1468: * the current index */
! 1469: if (axl_stream_prebuffer (stream))
! 1470: goto init_get_until;
! 1471: }
! 1472:
! 1473: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "matched_chunk=%d", iterator);
! 1474:
! 1475: /* report which is the chunk being
! 1476: * matched by the expresion */
! 1477: stream->chunk_matched = iterator;
! 1478:
! 1479: matched_return_result:
! 1480:
! 1481: /* result is found from last stream
! 1482: * index read up to index */
! 1483: if (stream->last_chunk != NULL) {
! 1484: axl_free (stream->last_chunk);
! 1485: stream->last_chunk = NULL;
! 1486: }
! 1487:
! 1488: /* get a copy to the chunk to be returned */
! 1489: if (! stream->result_size) {
! 1490: if (stream->alloc != NULL)
! 1491: stream->last_chunk = stream->alloc (index + 1, stream->alloc_data);
! 1492: else
! 1493: stream->last_chunk = axl_new (char, index + 1);
! 1494: memcpy (stream->last_chunk, stream->stream + stream->stream_index, index);
! 1495: }else {
! 1496: /* *result_size = index;*/
! 1497: stream->result_size = index;
! 1498: string = stream->stream + stream->stream_index;
! 1499:
! 1500: /* set a termination mark */
! 1501: string [ index ] = 0;
! 1502: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
! 1503: "Nullify internally index=%d to avoid printf problems", index);
! 1504: }
! 1505:
! 1506: /* in the case a memory chunk is being read */
! 1507: if (stream->accept_terminator)
! 1508: stream->stream_index += length;
! 1509: stream->stream_index += index;
! 1510: stream->global_index += index;
! 1511: stream->previous_inspect = 0;
! 1512:
! 1513:
! 1514: /* return allocated result */
! 1515: if (! stream->result_size)
! 1516: return stream->last_chunk;
! 1517:
! 1518: /* return reference result */
! 1519: return string;
! 1520: } /* end if */
! 1521:
! 1522: /* it seems that the chunk wasn't found */
! 1523: index++;
! 1524:
! 1525: }while (axl_true);
! 1526:
! 1527: /* return a NULL chunk. */
! 1528: return NULL;
! 1529: }
! 1530:
! 1531: /**
! 1532: * @brief Allows to perform the same operation like \ref
! 1533: * axl_stream_get_untilv but providing an already initialized and
! 1534: * opened std arg.
! 1535: *
! 1536: * This function is in fact, used by \ref axl_stream_get_untilv.
! 1537: *
! 1538: * @param stream The stream where the operation will be peformed.
! 1539: *
! 1540: * @param valid_chars The valid chars set to be used while reading
! 1541: * data.
! 1542: *
! 1543: * @param chunk_matched An optional value where the matched chunk will
! 1544: * be reported.
! 1545: *
! 1546: * @param accept_terminator Configure if terminator read should be
! 1547: * accepted or only the chunk read.
! 1548: *
! 1549: * @param result_size Allows to notify the caller with the chunk size
! 1550: * that is being returned by the function.
! 1551: *
! 1552: * @param chunk_num How many terminators are configured.
! 1553: *
! 1554: * @param args The list of terminators.
! 1555: *
! 1556: * @return The chunk read or NULL if fails.
! 1557: */
! 1558: char * axl_stream_get_untilv (axlStream * stream,
! 1559: char * valid_chars,
! 1560: int * chunk_matched,
! 1561: axl_bool accept_terminator,
! 1562: int * result_size,
! 1563: int chunk_num,
! 1564: va_list args)
! 1565: {
! 1566: char * result;
! 1567:
! 1568:
! 1569: /* check max inspected chunks */
! 1570: if (chunk_num > MAX_INSPECTED_CHUNKS) {
! 1571: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "unable to parse stream for the number of chunks to recognize. Max number supported is %d, but received %d",
! 1572: MAX_INSPECTED_CHUNKS, stream->chunk_num);
! 1573: return NULL;
! 1574: }
! 1575:
! 1576: /* configure variables for the operation */
! 1577: stream->valid_chars = valid_chars;
! 1578: stream->accept_terminator = accept_terminator;
! 1579: stream->result_size = (result_size != NULL);
! 1580: stream->chunk_num = chunk_num;
! 1581:
! 1582: /* call to current implementation */
! 1583: result = __axl_stream_get_untilv_wide (stream, args);
! 1584:
! 1585: /* check for returning references */
! 1586: if (result_size != NULL)
! 1587: *result_size = stream->result_size;
! 1588: if (chunk_matched != NULL)
! 1589: *chunk_matched = stream->chunk_matched;
! 1590:
! 1591: /* return string matched */
! 1592: return result;
! 1593: }
! 1594:
! 1595:
! 1596: /**
! 1597: * @brief Returns current index status for the given stream.
! 1598: *
! 1599: * This function could be used to get current index being read for the
! 1600: * stream received.
! 1601: *
! 1602: * @param stream The stream where the index will be reported.
! 1603: *
! 1604: * @return The index or -1 if fails.
! 1605: */
! 1606: int axl_stream_get_index (axlStream * stream)
! 1607: {
! 1608: axl_return_val_if_fail (stream, -1);
! 1609:
! 1610: return stream->stream_index;
! 1611: }
! 1612:
! 1613: /**
! 1614: * @brief Returns current global index for the device being streamed.
! 1615: *
! 1616: * @param stream The stream where the data is being requested.
! 1617: *
! 1618: * @return The index or -1 if fails.
! 1619: */
! 1620: int axl_stream_get_global_index (axlStream * stream)
! 1621: {
! 1622: axl_return_val_if_fail (stream, -1);
! 1623:
! 1624: return stream->global_index;
! 1625: }
! 1626:
! 1627: /**
! 1628: * @brief Allows to get current stream size.
! 1629: *
! 1630: * @param stream The stream where the stream size will be returned.
! 1631: *
! 1632: * @return The stream size or -1 if fails.
! 1633: */
! 1634: int axl_stream_get_size (axlStream * stream)
! 1635: {
! 1636: axl_return_val_if_fail (stream, -1);
! 1637:
! 1638: return stream->stream_size;
! 1639: }
! 1640:
! 1641: /**
! 1642: * @brief Allows to get current status of the given stream, taking the
! 1643: * current index, getting an amount of <b>count</b> bytes before and
! 1644: * after the given index.
! 1645: *
! 1646: * This function is mainly used to get a piece of the stream at the
! 1647: * given position while reporting errors. This allows to show the
! 1648: * piece of xml that is failing.
! 1649: *
! 1650: * The string return must not be deallocated. Value returned is
! 1651: * actually managed by the stream object associated.
! 1652: *
! 1653: * @param stream The stream where the near to operation will be performed.
! 1654: * @param count The amount of bytes to take.
! 1655: *
! 1656: * @return A string that is taken counting bytes with provided
! 1657: * <b>count</b> value starting from the index. Stream provided must be
! 1658: * not NULL and count value must be greater than 0.
! 1659: */
! 1660: const char * axl_stream_get_near_to (axlStream * stream, int count)
! 1661: {
! 1662: int first_index;
! 1663: int last_index;
! 1664:
! 1665: axl_return_val_if_fail (stream, NULL);
! 1666: axl_return_val_if_fail (count > 0, NULL);
! 1667:
! 1668: /* get first index */
! 1669: if ((stream->stream_index - count) <= 0)
! 1670: first_index = 0;
! 1671: else
! 1672: first_index = stream->stream_index - count;
! 1673:
! 1674: /* get last index */
! 1675: if ((stream->stream_index + count) >= (stream->stream_size - 1) )
! 1676: last_index = (stream->stream_size) - first_index;
! 1677: else
! 1678: last_index = (stream->stream_index + count) - first_index;
! 1679:
! 1680: /* release previous near to chunk */
! 1681: if (stream->last_near_to != NULL)
! 1682: axl_free (stream->last_near_to);
! 1683:
! 1684: stream->last_near_to = axl_new (char, last_index + 1);
! 1685: memcpy (stream->last_near_to, stream->stream + first_index, last_index);
! 1686:
! 1687: /* return current near to operation */
! 1688: return stream->last_near_to;
! 1689:
! 1690: }
! 1691:
! 1692: /**
! 1693: * @brief Allows to get the following <b>count</b> bytes read from the
! 1694: * stream.
! 1695: *
! 1696: * @param stream The stream where the operation will be performed.
! 1697: * @param count How many bytes to get from the stream.
! 1698: *
! 1699: * @return A string referece, containing the first <b>count</b> bytes
! 1700: * or NULL if fails. Reference returned shouldn't be deallocated.
! 1701: */
! 1702: const char * axl_stream_get_following (axlStream * stream, int count)
! 1703: {
! 1704: axl_return_val_if_fail (stream, NULL);
! 1705:
! 1706: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "getting next characters from stream: index=%d size=%d",
! 1707: stream->stream_index, stream->stream_size);
! 1708:
! 1709: /* check index */
! 1710: if (stream->stream_index >= stream->stream_size)
! 1711: return NULL;
! 1712:
! 1713: if ((count + stream->stream_index) > stream->stream_size) {
! 1714: count = stream->stream_size - stream->stream_index;
! 1715: }
! 1716:
! 1717: /* free previously allocated memory */
! 1718: if (stream->last_get_following != NULL)
! 1719: axl_free (stream->last_get_following);
! 1720:
! 1721: /* copy stream content */
! 1722: stream->last_get_following = axl_new (char, count + 1);
! 1723: memcpy (stream->last_get_following, stream->stream + stream->stream_index, count);
! 1724:
! 1725: /* return reference created */
! 1726: return stream->last_get_following;
! 1727: }
! 1728:
! 1729: typedef struct _AxlStreamAssociatedData {
! 1730: axlPointer data;
! 1731: axlDestroyFunc destroy_func;
! 1732: axl_bool free_on_finish;
! 1733: }AxlStreamAssociatedData;
! 1734:
! 1735:
! 1736: /**
! 1737: * @internal
! 1738: *
! 1739: * Internal deallocation function to reclaim memory used by the \ref
! 1740: * AxlStreamAssociatedData.
! 1741: *
! 1742: * @param data The data to be deallocated.
! 1743: */
! 1744: void __stream_associated_data_free (AxlStreamAssociatedData * data)
! 1745: {
! 1746: /* do nothing if NULL is received */
! 1747: if (data == NULL)
! 1748: return;
! 1749:
! 1750: /* destroy associated data used provided destroy function */
! 1751: if (data->destroy_func != NULL && data->data != NULL)
! 1752: data->destroy_func (data->data);
! 1753:
! 1754: /* now free the node it self */
! 1755: axl_free (data);
! 1756:
! 1757: return;
! 1758: }
! 1759:
! 1760: /**
! 1761: * @brief Associates the given \ref axlPointer with the given stream to be
! 1762: * life-time dependant.
! 1763: *
! 1764: * While performing the XML parsing, errors will be produced. This
! 1765: * function ensures that the axlDoc document will be released if the
! 1766: * stream is also released.
! 1767: *
! 1768: * This not only reduces the possibility to produce a memory leak also
! 1769: * allows to write less code.
! 1770: *
! 1771: * Once the stream is not useful and it is required to be released,
! 1772: * but not doing so with the \ref axlDoc instance, a call to \ref
! 1773: * axl_stream_unlink is also required.
! 1774: *
! 1775: * @param stream The axlStream where the document will be linked to.
! 1776: *
! 1777: * @param element The element to link (may a axlDoc or a axlDtd).
! 1778: *
! 1779: * @param func The function to call once the stream is released.
! 1780: */
! 1781: void axl_stream_link (axlStream * stream,
! 1782: axlPointer element,
! 1783: axlDestroyFunc func)
! 1784: {
! 1785: /* call to base implementation */
! 1786: axl_stream_link_full (stream, element, func, axl_false);
! 1787:
! 1788: return;
! 1789: }
! 1790:
! 1791: /**
! 1792: * @brief Allows to associate data references with a destroy function,
! 1793: * like \ref axl_stream_link, but ensuring the object reference will
! 1794: * be released once finished the axl stream, no mather if the
! 1795: * application code calls to \ref axl_stream_unlink.
! 1796: *
! 1797: * @param stream The axlStream where the document will be linked to.
! 1798: *
! 1799: * @param element The element to link (may a axlDoc or a axlDtd).
! 1800: *
! 1801: *
! 1802: * @param func The function to call once the stream is released.
! 1803: *
! 1804: * @param free_on_finish axl_true to make the reference to be released on
! 1805: * \ref axlStream deallocation. Providing \ref axl_false to this value is
! 1806: * equivalent to call to \ref axl_stream_link directly.
! 1807: */
! 1808: void axl_stream_link_full (axlStream * stream,
! 1809: axlPointer element,
! 1810: axlDestroyFunc func,
! 1811: axl_bool free_on_finish)
! 1812: {
! 1813: AxlStreamAssociatedData * data;
! 1814: axl_return_if_fail (stream);
! 1815: axl_return_if_fail (element);
! 1816: axl_return_if_fail (func);
! 1817:
! 1818: /* that's all */
! 1819: if (stream->elements_linked == NULL)
! 1820: stream->elements_linked = axl_list_new (axl_list_always_return_1,
! 1821: (axlDestroyFunc) __stream_associated_data_free);
! 1822:
! 1823: /* create the data to be stored */
! 1824: data = axl_new (AxlStreamAssociatedData, 1);
! 1825: data->data = element;
! 1826: data->destroy_func = func;
! 1827: data->free_on_finish = free_on_finish;
! 1828:
! 1829: /* add the item to be destroy once the stream is unrefered */
! 1830: axl_list_add (stream->elements_linked, data);
! 1831:
! 1832: return;
! 1833: }
! 1834:
! 1835: /**
! 1836: * @brief Unlinks the associated \ref axlDoc instance.
! 1837: *
! 1838: * @param stream The stream where the operation will be performed.
! 1839: */
! 1840: void axl_stream_unlink (axlStream * stream)
! 1841: {
! 1842: int iterator;
! 1843: AxlStreamAssociatedData * data;
! 1844:
! 1845: axl_return_if_fail (stream);
! 1846:
! 1847: /* clear document association */
! 1848: iterator = 0;
! 1849: while (iterator < axl_list_length (stream->elements_linked)) {
! 1850: /* get a referece to the node to destroy */
! 1851: data = axl_list_get_nth (stream->elements_linked, iterator);
! 1852: /* clear it */
! 1853: if (! data->free_on_finish) {
! 1854: data->data = NULL;
! 1855: data->destroy_func = NULL;
! 1856: }
! 1857: iterator++;
! 1858: }
! 1859: return;
! 1860: }
! 1861:
! 1862: /**
! 1863: * @brief Allows to deallocate memory used by the \ref axlStream
! 1864: * received.
! 1865: *
! 1866: * @param stream The stream to be deallocated.
! 1867: */
! 1868: void axl_stream_free (axlStream * stream)
! 1869: {
! 1870: axl_return_if_fail (stream);
! 1871:
! 1872: /* release memory */
! 1873: axl_free (stream->stream);
! 1874:
! 1875: /* release associated document is defined. */
! 1876: if (stream->elements_linked)
! 1877: axl_list_free (stream->elements_linked);
! 1878:
! 1879: /* releaset last chunk */
! 1880: if (stream->last_chunk != NULL)
! 1881: axl_free (stream->last_chunk);
! 1882:
! 1883: /* releaset last near to */
! 1884: if (stream->last_near_to != NULL)
! 1885: axl_free (stream->last_near_to);
! 1886:
! 1887: /* releaset last get following */
! 1888: if (stream->last_get_following != NULL)
! 1889: axl_free (stream->last_get_following);
! 1890:
! 1891: if (stream->fd > 0) {
! 1892: /* close file descriptor if defined */
! 1893: close (stream->fd);
! 1894: }
! 1895:
! 1896: /* free memory allocated for chunk matching */
! 1897: axl_free (stream->chunks);
! 1898:
! 1899: /* free lengths */
! 1900: axl_free (stream->lengths);
! 1901:
! 1902: /* free temporal buffer */
! 1903: axl_free (stream->temp);
! 1904: axl_free (stream->decode_temp);
! 1905: axl_free (stream->source_encoding);
! 1906:
! 1907: /* release memory allocated by the stream received. */
! 1908: axl_free (stream);
! 1909:
! 1910: return;
! 1911: }
! 1912:
! 1913:
! 1914: /**
! 1915: * @brief Allows to check if the given chunk is a white space in the
! 1916: * same of the XML 1.0 Third edition.
! 1917: *
! 1918: * The XML standard understand the "white space", also reffered as S,
! 1919: * as the following characters: \\x20 (the white space itself), \\n, \\r
! 1920: * and \\t.
! 1921: *
! 1922: * This function allows to check if the given chunk contains a white
! 1923: * space, in the previous sense.
! 1924: *
! 1925: * @param chunk The chunk to check
! 1926: *
! 1927: * @return axl_true if the chunk contains a white space or axl_false
! 1928: * if not.
! 1929: */
! 1930: axl_bool axl_stream_is_white_space (char * chunk)
! 1931: {
! 1932: /* do not complain about receive a null refernce chunk */
! 1933: if (chunk == NULL)
! 1934: return axl_false;
! 1935:
! 1936: if (chunk[0] == ' ')
! 1937: return axl_true;
! 1938: if (chunk[0] == '\n')
! 1939: return axl_true;
! 1940: if (chunk[0] == '\t')
! 1941: return axl_true;
! 1942: if (chunk[0] == '\r')
! 1943: return axl_true;
! 1944:
! 1945: /* no white space was found */
! 1946: return axl_false;
! 1947: }
! 1948:
! 1949: /**
! 1950: * @brief Support function which consumes white spaces in the W3C
! 1951: * sense.
! 1952: *
! 1953: * @param stream The stream where the operation will be performed.
! 1954: *
! 1955: * @return axl_true if more white spaces could be consumed, axl_false
! 1956: * if not.
! 1957: */
! 1958: void axl_stream_consume_white_spaces (axlStream * stream)
! 1959: {
! 1960: /* get how many bytes remains to be read */
! 1961: int remains = stream->stream_size - stream->stream_index;
! 1962:
! 1963: while (axl_true) {
! 1964: /* decrase the number of bytes remaining to be read
! 1965: * and check if it is zero or less than zero to
! 1966: * prebuffer. NOTE: remains could be 1 (remains one
! 1967: * byte) making it possible to consume one byte
! 1968: * more */
! 1969: remains--;
! 1970: if (remains < 0) {
! 1971: /* we fall outside the stream, so a prebuffer
! 1972: * operation is required */
! 1973: if (! axl_stream_prebuffer (stream))
! 1974: return;
! 1975:
! 1976: /* update remains value but this time removing
! 1977: * one unit (the unit to be consumed at the
! 1978: * next sentence) */
! 1979: remains = stream->stream_size - stream->stream_index - 1;
! 1980: }
! 1981:
! 1982: /* check for a white space */
! 1983: if ((stream->stream[stream->stream_index] == ' ') ||
! 1984: (stream->stream[stream->stream_index] == '\n') ||
! 1985: (stream->stream[stream->stream_index] == '\t') ||
! 1986: (stream->stream[stream->stream_index] == '\r')) {
! 1987:
! 1988: /* update internal indexes */
! 1989: stream->stream_index++;
! 1990: stream->global_index++;
! 1991: stream->previous_inspect = 0;
! 1992: }else {
! 1993: /* return */
! 1994: return;
! 1995: }
! 1996:
! 1997: } /* end while */
! 1998:
! 1999: return;
! 2000: }
! 2001:
! 2002:
! 2003:
! 2004: /**
! 2005: * @internal
! 2006: * @brief Allows to compare two strings pointed by
! 2007: *
! 2008: * @param chunk1 The string to be compared.
! 2009: *
! 2010: * @param chunk2 The second string to be compared.
! 2011: *
! 2012: * @param size The amount of bytes to be compared for the two incoming
! 2013: * values.
! 2014: *
! 2015: * @return axl_true if both string are equal, axl_false if not. If
! 2016: * some value provided is NULL or the size to compare is not greater
! 2017: * than 0 the function will return axl_false directly.
! 2018: */
! 2019: axl_bool axl_stream_cmp (const char * chunk1, const char * chunk2, int size)
! 2020: {
! 2021: /* perform some environmental condition checking */
! 2022: if (chunk1 == NULL)
! 2023: return axl_false;
! 2024: if (chunk2 == NULL)
! 2025: return axl_false;
! 2026: if (size < 0)
! 2027: return axl_false;
! 2028:
! 2029: /* report current comparation status */
! 2030: if ((chunk1[0] == chunk2[0])) {
! 2031: if ((size == 1) ||
! 2032: (axl_memcmp (chunk1 + 1, chunk2 + 1, size -1))) {
! 2033: return axl_true;
! 2034: } /* end if */
! 2035: } /* end if */
! 2036:
! 2037: return axl_false;
! 2038: }
! 2039:
! 2040: /**
! 2041: * @brief Provides the same function like axl_stream_cmp but
! 2042: * ignoring the case of the characters (case insensitive manner).
! 2043: *
! 2044: * @param chunk1 The string to be compared.
! 2045: *
! 2046: * @param chunk2 The second string to be compared.
! 2047: *
! 2048: * @param size The amount of bytes to be compared for the two incoming
! 2049: * values.
! 2050: *
! 2051: * @return axl_true if both string are equal, axl_false if not. If
! 2052: * some value provided is NULL or the size to compare is not greater
! 2053: * than 0 the function will return axl_false directly.
! 2054: */
! 2055: axl_bool axl_stream_casecmp (const char * chunk1, const char * chunk2, int size)
! 2056: {
! 2057: /* perform some environmental condition checking */
! 2058: if (chunk1 == NULL)
! 2059: return axl_false;
! 2060: if (chunk2 == NULL)
! 2061: return axl_false;
! 2062: if (size < 0)
! 2063: return axl_false;
! 2064:
! 2065: /* returh if both strings are equal. */
! 2066: #if defined(AXL_OS_WIN32)
! 2067: return _strnicmp (chunk1, chunk2, size) == 0;
! 2068: #else
! 2069: return strncasecmp (chunk1, chunk2, size) == 0;
! 2070: #endif
! 2071: }
! 2072:
! 2073: /**
! 2074: * @internal
! 2075: *
! 2076: * @brief Allows to check if the provided stream could support
! 2077: * checking a chunk of, at least, the provided inspected size.
! 2078: *
! 2079: * @param stream The stream where the inspected operation is being
! 2080: * requested.
! 2081: *
! 2082: * @param inspected_size The size to inspected, that is being
! 2083: * requested to be checked on the given stream.
! 2084: *
! 2085: * @return \ref axl_true if the provided chunk falls out side the
! 2086: * stream boundaries, or axl_false if requested inspected size could
! 2087: * be supported.
! 2088: */
! 2089: axl_bool axl_stream_fall_outside (axlStream * stream, int inspected_size)
! 2090: {
! 2091: /* if the content is inside memory, check it */
! 2092: if (fall_out_side_checking (stream, inspected_size)) {
! 2093: return (! axl_stream_prebuffer (stream));
! 2094: }
! 2095:
! 2096: /* otherwise, axl_false is returned */
! 2097: return axl_false;
! 2098: }
! 2099:
! 2100: /**
! 2101: * @internal
! 2102: *
! 2103: * @brief Allows to check if the given stream have as a next element
! 2104: * the provided chunk.
! 2105: *
! 2106: * @param stream The stream where the operation will be performed.
! 2107: * @param chunk The chunk to check
! 2108: *
! 2109: * @return Returns axl_true if the given stream contains the value requested
! 2110: * or axl_false if not.
! 2111: */
! 2112: axl_bool axl_stream_check (axlStream * stream, char * chunk, int inspected_size)
! 2113: {
! 2114: int iterator;
! 2115:
! 2116: /* call to the internal implementation of axl_memcmp */
! 2117: _memcmp(iterator,chunk,(stream->stream + stream->stream_index), inspected_size);
! 2118:
! 2119: }
! 2120:
! 2121: /**
! 2122: * @brief Allows to get current status of the stream.
! 2123: *
! 2124: * If the is exhausted and have no more data to be read.
! 2125: *
! 2126: * @param stream The stream that is being checked.
! 2127: *
! 2128: * @return axl_true if the stream is exhausted or axl_false if not.
! 2129: */
! 2130: axl_bool axl_stream_remains (axlStream * stream)
! 2131: {
! 2132: axl_return_val_if_fail (stream, axl_false);
! 2133:
! 2134:
! 2135: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking for stream status with stream index=%d and stream size=%d",
! 2136: stream->stream_index, stream->stream_size);
! 2137:
! 2138: /* check if the stream is exhausted */
! 2139: if (stream->stream_index >= (stream->stream_size)) {
! 2140:
! 2141: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "prebufferring from remains");
! 2142:
! 2143: /* in the case the stream is exhausted, try to read
! 2144: * more content from the streaming */
! 2145: return axl_stream_prebuffer (stream);
! 2146: }
! 2147: return axl_true;
! 2148: }
! 2149:
! 2150:
! 2151: /* @} */
! 2152:
! 2153: /**
! 2154: * \defgroup axl_string_module Axl String: String functions provided by the Axl Stream module.
! 2155: */
! 2156:
! 2157: /**
! 2158: * \addtogroup axl_string_module
! 2159: * @{
! 2160: */
! 2161:
! 2162: /**
! 2163: *
! 2164: * @brief Allows to trim the provided chunk, removing all white spaces
! 2165: * (returns, white spaces, carry return and tabulars) that comes as
! 2166: * preffix and suffix for the string provided, referenced by chunk.
! 2167: *
! 2168: * The function retuns the reference to the new chunk already
! 2169: * translated. The function doesn't perform any memory allocation. It
! 2170: * uses the memory already used to hold the chunk provided, returning
! 2171: * a pointer for the first item that is not a white space and
! 2172: * nullifing the first item that is a white space behind the chunk.
! 2173: *
! 2174: * This function is particular useful while getting the content
! 2175: *
! 2176: * @param chunk The chunk to trim.
! 2177: *
! 2178: */
! 2179: void axl_stream_trim (char * chunk)
! 2180: {
! 2181: /* call to trim */
! 2182: axl_stream_trim_with_size (chunk, NULL);
! 2183:
! 2184: return;
! 2185: }
! 2186:
! 2187: /**
! 2188: * @brief The function works like \ref axl_stream_trim, but providing
! 2189: * the count of bytes trimmed from the string.
! 2190: *
! 2191: * @param chunk The chunk to trim.
! 2192: *
! 2193: * @param trimmed An optional reference that returns the count of bytes
! 2194: * trimmed by the operation.
! 2195: */
! 2196: void axl_stream_trim_with_size (char * chunk, int * trimmed)
! 2197: {
! 2198: int iterator;
! 2199: int iterator2;
! 2200: int end;
! 2201: int total;
! 2202:
! 2203: /* perform some environment check */
! 2204: axl_return_if_fail (chunk);
! 2205:
! 2206: /* check empty string received */
! 2207: if (strlen (chunk) == 0) {
! 2208: if (trimmed)
! 2209: *trimmed = 0;
! 2210: return;
! 2211: }
! 2212:
! 2213: /* check the amount of white spaces to remove from the
! 2214: * begin */
! 2215: iterator = 0;
! 2216: while (chunk[iterator] != 0) {
! 2217:
! 2218: /* check that the iterator is not pointing to a white
! 2219: * space */
! 2220: if (! axl_stream_is_white_space (chunk + iterator))
! 2221: break;
! 2222:
! 2223: /* update the iterator */
! 2224: iterator++;
! 2225: }
! 2226:
! 2227: /* check for the really basic case where an empty string is found */
! 2228: if (iterator == strlen (chunk)) {
! 2229: /* an empty string, trim it all */
! 2230: chunk [0] = 0;
! 2231: if (trimmed)
! 2232: *trimmed = iterator;
! 2233: return;
! 2234: } /* end if */
! 2235:
! 2236: /* now get the position for the last valid character in the
! 2237: * chunk */
! 2238: total = strlen (chunk) -1;
! 2239: end = total;
! 2240: while (chunk[end] != 0) {
! 2241:
! 2242: /* stop if a white space is found */
! 2243: if (! axl_stream_is_white_space (chunk + end)) {
! 2244: break;
! 2245: }
! 2246:
! 2247: /* update the iterator to eat the next white space */
! 2248: end--;
! 2249: }
! 2250:
! 2251: /* the number of items trimmed */
! 2252: total -= end;
! 2253: total += iterator;
! 2254:
! 2255: /* copy the exact amount of non white spaces items */
! 2256: iterator2 = 0;
! 2257: while (iterator2 < (end - iterator + 1)) {
! 2258: /* copy the content */
! 2259: chunk [iterator2] = chunk [iterator + iterator2];
! 2260:
! 2261: /* update the iterator */
! 2262: iterator2++;
! 2263: }
! 2264: chunk [ end - iterator + 1] = 0;
! 2265:
! 2266: if (trimmed != NULL)
! 2267: *trimmed = total;
! 2268:
! 2269: /* return the result reference */
! 2270: return;
! 2271: }
! 2272:
! 2273: /**
! 2274: * @brief Allows to remote occurences of value from the provided
! 2275: * string (chunk).
! 2276: *
! 2277: * The function do not allocate new memory for the result. All
! 2278: * operations are applied to the string received (chunk).
! 2279: *
! 2280: * The idea behind the functions is to allow removing values from the
! 2281: * string, joining remaining content. For example, removing "-" from
! 2282: * the string "iso-8859-15" yields "iso885915".
! 2283: *
! 2284: * @param chunk The string that holds values to be removed.
! 2285: *
! 2286: * @param value The value to be removed.
! 2287: *
! 2288: * @param first If only the first ocurrence of value must be removed,
! 2289: * otherwise all ocurrences will be removed from the string.
! 2290: */
! 2291: void axl_stream_remove (char * chunk, const char * value, axl_bool first)
! 2292: {
! 2293: int iterator;
! 2294: int iterator2;
! 2295: int length;
! 2296: int v_length;
! 2297:
! 2298: axl_return_if_fail (chunk);
! 2299: axl_return_if_fail (value);
! 2300:
! 2301: /* get lengths */
! 2302: length = strlen (chunk);
! 2303: v_length = strlen (value);
! 2304:
! 2305: /* check for basic cases */
! 2306: if (length == v_length) {
! 2307: /* check if both strings are equal, then nullify */
! 2308: if (axl_cmp (chunk, value))
! 2309: chunk [0] = 0;
! 2310: return;
! 2311: } else if (length < v_length) {
! 2312: /* nothing to remove because parent string is too
! 2313: * small to store the content */
! 2314: return;
! 2315: } /* end if */
! 2316:
! 2317:
! 2318: /* locate the string */
! 2319: iterator = 0;
! 2320: while (iterator < length) {
! 2321: /* check if the string value was found */
! 2322: if (axl_memcmp (chunk + iterator, value, v_length)) {
! 2323: /* string found, move content remaining (if is found) */
! 2324: if ((length - iterator - v_length) > 0) {
! 2325: iterator2 = 0;
! 2326: while (iterator2 < (length - iterator - v_length)) {
! 2327: chunk [iterator + iterator2] = chunk [iterator + iterator2 + v_length];
! 2328: iterator2++;
! 2329: } /* end while */
! 2330: } /* end if */
! 2331:
! 2332: /* update length to the new value */
! 2333: length -= v_length;
! 2334:
! 2335: /* check to terminate for first ocurrence */
! 2336: if (first) {
! 2337: chunk [length] = 0;
! 2338: return;
! 2339: }
! 2340: continue;
! 2341: } /* end if */
! 2342:
! 2343: /* next position */
! 2344: iterator++;
! 2345: } /* end while */
! 2346:
! 2347: /* nullify and terminate */
! 2348: chunk [length] = 0;
! 2349: return;
! 2350: }
! 2351:
! 2352: /**
! 2353: * @brief Allows to copy the given chunk, supposing that is a properly
! 2354: * format C string that ends with a '\\0' value.
! 2355: *
! 2356: * This function allows to perform a copy for the given string. If a
! 2357: * copy limited by a size is required, use \ref axl_stream_strdup_n.
! 2358: *
! 2359: * @param chunk The chunk to copy
! 2360: *
! 2361: * @return A newly allocated string or NULL if fails.
! 2362: */
! 2363: char * axl_stream_strdup (const char * chunk)
! 2364: {
! 2365: char * result;
! 2366: int length;
! 2367:
! 2368: /* return NULL reference if a NULL reference is received */
! 2369: if (chunk == NULL)
! 2370: return NULL;
! 2371:
! 2372: length = strlen (chunk);
! 2373: result = axl_new (char, length + 1);
! 2374:
! 2375: memcpy (result, chunk, length);
! 2376:
! 2377: return result;
! 2378: }
! 2379:
! 2380: /**
! 2381: * @brief Allows to perform a copy for the <b>n</b> first bytes from
! 2382: * the <b>chunk</b> received.
! 2383: *
! 2384: * @param chunk The chunk to copy
! 2385: *
! 2386: * @param n How many bytes to copy from the given chunk.
! 2387: *
! 2388: * @return A newly allocated chunk, copied from the given chunk with a
! 2389: * size of <b>n</b> bytes. The function will check that the
! 2390: * <b>chunk</b> and the <b>n</b> values are not null and non-zero.
! 2391: */
! 2392: char * axl_stream_strdup_n (const char * chunk, int n)
! 2393: {
! 2394: char * result;
! 2395:
! 2396: axl_return_val_if_fail (chunk, NULL);
! 2397: axl_return_val_if_fail (n, NULL);
! 2398:
! 2399: result = axl_new (char, n + 1);
! 2400: memcpy (result, chunk, n);
! 2401:
! 2402: return result;
! 2403: }
! 2404:
! 2405: /**
! 2406: * @internal Allows to calculate the amount of memory required to
! 2407: * store the string that will representing the construction provided
! 2408: * by the printf-like format received and its arguments.
! 2409: *
! 2410: * @param format The printf-like format to be printed.
! 2411: *
! 2412: * @param args The set of arguments that the printf applies to.
! 2413: *
! 2414: * <i><b>NOTE:</b> not all printf specification is supported. Generally, the
! 2415: * following is supported: %s, %d, %f, %g, %ld, %lg and all
! 2416: * combinations that provides precision, number of items inside the
! 2417: * integer part, etc: %6.2f, %+2d, etc. An especial case not supported
! 2418: * is %lld, %llu and %llg.</i>
! 2419: *
! 2420: * @return Return the number of bytes that must be allocated to hold
! 2421: * the string (including the string terminator \0). If the format is
! 2422: * not correct or it is not properly formated according to the value
! 2423: * found at the argument set, the function will return -1.
! 2424: */
! 2425: int axl_stream_vprintf_len (const char * format, va_list args)
! 2426: {
! 2427: #if defined (AXL_OS_WIN32) && ! defined (__GNUC__)
! 2428: # if HAVE_VSCPRINTF
! 2429: if (format == NULL)
! 2430: return 0;
! 2431: return _vscprintf (format, args) + 1;
! 2432: # else
! 2433: char buffer[8192];
! 2434: if (format == NULL)
! 2435: return 0;
! 2436: return _vsnprintf (buffer, 8191, format, args) + 1;
! 2437: # endif
! 2438: #else
! 2439: /* gnu gcc case */
! 2440: if (format == NULL)
! 2441: return 0;
! 2442: return vsnprintf (NULL, 0, format, args) + 1;
! 2443: #endif
! 2444: }
! 2445:
! 2446: /**
! 2447: * @brief Allows to perform a printf operation on the provided buffer
! 2448: * (which must be allocated by the caller, and its size signaled by
! 2449: * buffer_size).
! 2450: *
! 2451: *
! 2452: * @param buffer The already allocated buffer to hold the result.
! 2453: *
! 2454: * @param buffer_size The size of the buffer provided.
! 2455: *
! 2456: * @param real_size Optional reference where the real space required
! 2457: * to hold the content will be placed. In cases where the content is
! 2458: * enough small to hold in the buffer, this value will contain the
! 2459: * same value as returned by the function. In the case the buffer
! 2460: * provide can't hold all the content, the function will return at
! 2461: * maximum (buffer_size - 1) bytes written, that is, all content that
! 2462: * was possible to be included plus a trailing \\0 to terminate the
! 2463: * string, and, if defined <i>real_size</i> variable, it will contain
! 2464: * the space that will be required.
! 2465: *
! 2466: * @param format The printf like format to use to create the content
! 2467: * to be placed at the buffer provided.
! 2468: *
! 2469: * @return The amount of bytes written. The function will return at
! 2470: * maximum buffer_size - 1 bytes written. Use <i>real_size</i>
! 2471: * variable to check if the function was able to write all content (if
! 2472: * real_size == value returned).
! 2473: */
! 2474: int axl_stream_printf_buffer (char * buffer,
! 2475: int buffer_size,
! 2476: int * real_size,
! 2477: const char * format, ...)
! 2478: {
! 2479: va_list args;
! 2480: int result;
! 2481: #if defined(AXL_OS_WIN32)
! 2482: int check_size;
! 2483: #endif
! 2484:
! 2485: /* check foramt and optn */
! 2486: if (format == NULL) {
! 2487: /* clean real size if it was defined */
! 2488: if (real_size)
! 2489: (*real_size) = 0;
! 2490: return 0;
! 2491: }
! 2492: /* open stdargs */
! 2493: va_start (args, format);
! 2494:
! 2495: # if defined (AXL_OS_WIN32)
! 2496: /* because undre windows all its string family of functions
! 2497: * return -1 in the case not enough espace is available, we
! 2498: * have to check first before calling to _vsnprintf */
! 2499: check_size = axl_stream_vprintf_len (format, args);
! 2500:
! 2501: /* call to close to avoid problems with amd64 platforms */
! 2502: va_end (args);
! 2503: va_start (args, format);
! 2504:
! 2505: /* windows case */
! 2506: result = _vsnprintf (buffer, buffer_size, format, args);
! 2507: if (result == -1) {
! 2508: /* nullify last character and update "result" to point
! 2509: * to the amount of data written (buffer - 1). */
! 2510: result = (buffer_size - 1);
! 2511: buffer[result] = 0;
! 2512: } /* end if */
! 2513: #else
! 2514: /* gnu gcc case */
! 2515: result = vsnprintf (buffer, buffer_size, format, args);
! 2516: #endif
! 2517: /* close stdarg */
! 2518: va_end (args);
! 2519:
! 2520: /* report real size required */
! 2521: if (real_size) {
! 2522: #if defined(AXL_OS_WIN32)
! 2523: (*real_size) = check_size - 1;
! 2524: #else
! 2525: (*real_size) = result;
! 2526: #endif
! 2527: } /* end if */
! 2528:
! 2529: /* limit result */
! 2530: if (result > (buffer_size - 1))
! 2531: result = (buffer_size - 1);
! 2532:
! 2533: return result;
! 2534: }
! 2535:
! 2536: /**
! 2537: * @internal Function that allows to get how many bytes will be
! 2538: * required to hold the format and the arguments provided.
! 2539: *
! 2540: * @param format The printf-like format followed by the rest of
! 2541: * arguments.
! 2542: *
! 2543: * @return Return the number of bytes that must be allocated to hold
! 2544: * the string (including the string terminator \0). If the format is
! 2545: * not correct or it is not properly formated according to the value
! 2546: * found at the argument set, the function will return -1.
! 2547: */
! 2548: int axl_stream_printf_len (const char * format, ...)
! 2549: {
! 2550: int result;
! 2551: va_list args;
! 2552:
! 2553: va_start (args, format);
! 2554:
! 2555: /* get the result */
! 2556: result = axl_stream_vprintf_len (format, args);
! 2557:
! 2558: va_end (args);
! 2559:
! 2560: return result;
! 2561: }
! 2562:
! 2563:
! 2564: /**
! 2565: * @brief Allows to produce an newly allocated string produced by the
! 2566: * chunk received plus arguments, using the printf-like format.
! 2567: *
! 2568: * @param chunk The chunk to copy.
! 2569: *
! 2570: * @return A newly allocated chunk.
! 2571: */
! 2572: char * axl_stream_strdup_printf (const char * chunk, ...)
! 2573: {
! 2574: char * result = NULL;
! 2575: va_list args;
! 2576:
! 2577: axl_return_val_if_fail (chunk, NULL);
! 2578:
! 2579: /* open std args */
! 2580: va_start (args, chunk);
! 2581:
! 2582: /* get the string */
! 2583: result = axl_stream_strdup_printfv (chunk, args);
! 2584:
! 2585: /* close std args */
! 2586: va_end (args);
! 2587:
! 2588: return result;
! 2589: }
! 2590:
! 2591: /**
! 2592: * @brief DEPRECATED: Allows to produce an string representing the
! 2593: * message hold by chunk with the parameters provided.
! 2594: *
! 2595: * @param chunk The message chunk to print.
! 2596: * @param args The arguments for the chunk.
! 2597: *
! 2598: * @return A newly allocated string.
! 2599: *
! 2600: * IMPLEMENTATION NOTE: This function have a fundamental bug due to
! 2601: * the design of va_list arguments under amd64 platforms. In short, a
! 2602: * function receiving a va_list argument can't use it twice. In you
! 2603: * are running amd64, check your axl_config.h did find
! 2604: * AXL_HAVE_VASPRINTF.
! 2605: */
! 2606: char * axl_stream_strdup_printfv (const char * chunk, va_list args)
! 2607: {
! 2608: /** IMPLEMENTATION NOTE: place update exarg_strdup_printfv
! 2609: * code in the case this code is updated **/
! 2610:
! 2611: #ifndef AXL_HAVE_VASPRINTF
! 2612: int size;
! 2613: #endif
! 2614: char * result = NULL;
! 2615: int new_size = -1;
! 2616:
! 2617: axl_return_val_if_fail (chunk, NULL);
! 2618:
! 2619: #ifdef AXL_HAVE_VASPRINTF
! 2620: /* do the operation using the GNU extension */
! 2621: new_size = vasprintf (&result, chunk, args);
! 2622: #else
! 2623: /* get the amount of memory to be allocated */
! 2624: size = axl_stream_vprintf_len (chunk, args);
! 2625:
! 2626: /* check result */
! 2627: if (size == -1) {
! 2628: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "unable to calculate the amount of memory for the strdup_printf operation");
! 2629: return NULL;
! 2630: } /* end if */
! 2631:
! 2632: /* allocate memory */
! 2633: result = axl_new (char, size + 2);
! 2634:
! 2635: /* copy current size */
! 2636: # if defined(AXL_OS_WIN32) && ! defined (__GNUC__)
! 2637: new_size = _vsnprintf_s (result, size + 1, size, chunk, args);
! 2638: # else
! 2639: new_size = vsnprintf (result, size + 1, chunk, args);
! 2640: # endif
! 2641: #endif
! 2642: /* return the result */
! 2643: return result;
! 2644: }
! 2645:
! 2646: /**
! 2647: * @brief Allows to create a newly allocated chunk, providing its
! 2648: * values as a printf call function, but also returning the chunk
! 2649: * size.
! 2650: *
! 2651: * This function works like \ref axl_stream_strdup_printf, but
! 2652: * providing an integer reference where the result chunk length will
! 2653: * be returned.
! 2654: *
! 2655: * @param chunk The printf chunk format to allocate.
! 2656: *
! 2657: * @param chunk_size A reference to fill the chunk lenght.
! 2658: *
! 2659: * @return A newly allocated chunk.
! 2660: */
! 2661: char * axl_stream_strdup_printf_len (const char * chunk, int * chunk_size, ...)
! 2662: {
! 2663: #ifndef AXL_HAVE_VASPRINTF
! 2664: int size;
! 2665: #endif
! 2666: int new_size;
! 2667: char * result;
! 2668: va_list args;
! 2669:
! 2670: axl_return_val_if_fail (chunk, NULL);
! 2671:
! 2672: /* open std args */
! 2673: va_start (args, chunk_size);
! 2674:
! 2675: #ifdef AXL_HAVE_VASPRINTF
! 2676: /* do the operation using the GNU extension */
! 2677: new_size = vasprintf (&result, chunk, args);
! 2678:
! 2679: /* reopen to avoid amd64 bug */
! 2680: va_end (args);
! 2681: va_start (args, chunk_size);
! 2682: #else
! 2683: /* get the amount of memory to be allocated */
! 2684: size = axl_stream_vprintf_len (chunk, args);
! 2685:
! 2686: /* reopen to avoid amd64 bug */
! 2687: va_end (args);
! 2688: va_start (args, chunk_size);
! 2689:
! 2690: /* check result */
! 2691: if (size == -1) {
! 2692: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "unable to calculate the amount of memory for the strdup_printf operation");
! 2693: return NULL;
! 2694: } /* end if */
! 2695:
! 2696: /* allocate memory */
! 2697: result = axl_new (char, size + 2);
! 2698:
! 2699: /* copy current size */
! 2700: #if defined(AXL_OS_WIN32) && ! defined (__GNUC__)
! 2701: new_size = _vsnprintf_s (result, size + 1, size, chunk, args);
! 2702: #else
! 2703: new_size = vsnprintf (result, size + 1, chunk, args);
! 2704: #endif
! 2705: #endif
! 2706:
! 2707: /* close std args */
! 2708: va_end (args);
! 2709:
! 2710: /* fill the chunk size result */
! 2711: if (chunk_size != NULL)
! 2712: *chunk_size = new_size;
! 2713:
! 2714: return result;
! 2715: }
! 2716:
! 2717: /**
! 2718: * @brief Allows to split the provided chunk, into several pieces that
! 2719: * are separated by the separator (or separators) provided.
! 2720: *
! 2721: * The function will try to split the chunk provide using the
! 2722: * separator provided, and optionally, all separators provided.
! 2723: *
! 2724: * Here is an example:
! 2725: * \code
! 2726: * char ** result;
! 2727: *
! 2728: * // split the provided value using the ':', ';' and ',' as separators.
! 2729: * result = axl_stream_split (value, 3, ":", ";", ",");
! 2730: * \endcode
! 2731: *
! 2732: * The value returned must be deallocated using \ref axl_stream_freev.
! 2733: *
! 2734: * @param chunk The chunk to split.
! 2735: *
! 2736: * @param separator_num The number os separators to be used while
! 2737: * spliting the chunk.
! 2738: *
! 2739: * @return A newly allocated string, that must be deallocated by using
! 2740: * \ref axl_stream_freev. The function will return a NULL if the chunk
! 2741: * or the separators provided are NULL.
! 2742: *
! 2743: * NOTE: See also \ref axl_split.
! 2744: */
! 2745: char ** axl_stream_split (const char * chunk, int separator_num, ...)
! 2746: {
! 2747: va_list args;
! 2748: char ** separators;
! 2749: char ** result;
! 2750: int iterator;
! 2751: int index;
! 2752: int previous_index;
! 2753: int count = 0;
! 2754: int length = 0;
! 2755:
! 2756: /* check received values */
! 2757: axl_return_val_if_fail (chunk, NULL);
! 2758: axl_return_val_if_fail (separator_num > 0, NULL);
! 2759:
! 2760: separators = axl_new (char *, separator_num + 1);
! 2761: iterator = 0;
! 2762: va_start (args, separator_num);
! 2763:
! 2764: /* get all separators to be used */
! 2765: while (iterator < separator_num) {
! 2766: separators[iterator] = va_arg (args, char *);
! 2767: iterator++;
! 2768: } /* end if */
! 2769:
! 2770: va_end (args);
! 2771:
! 2772: /* now, count the number of strings that we will get by
! 2773: * separating the string into several pieces */
! 2774: index = 0;
! 2775: while (*(chunk + index) != 0) {
! 2776:
! 2777: /* reset the iterator */
! 2778: iterator = 0;
! 2779: while (iterator < separator_num) {
! 2780:
! 2781: /* compare the current index with the current
! 2782: * separator */
! 2783: length = strlen (separators[iterator]);
! 2784: if (axl_memcmp (chunk + index, separators[iterator], length)) {
! 2785:
! 2786: /* update items found */
! 2787: count++;
! 2788:
! 2789: /* update index to skip the item found */
! 2790: index += length - 1; /* make the last index to be captured the the -1 */
! 2791:
! 2792: /* break the loop */
! 2793: break;
! 2794: }
! 2795: iterator++;
! 2796: }
! 2797:
! 2798: /* update the index to the next item */
! 2799: index++;
! 2800: } /* end if */
! 2801:
! 2802: /* create the result that will hold items separated */
! 2803: result = axl_new (char *, count + 2);
! 2804:
! 2805: /* now copy items found */
! 2806: count = 0;
! 2807: index = 0;
! 2808:
! 2809: /* remember previous_index */
! 2810: previous_index = index;
! 2811: while (*(chunk + index) != 0) {
! 2812:
! 2813: /* reset the iterator */
! 2814: iterator = 0;
! 2815: while (iterator < separator_num) {
! 2816:
! 2817: /* compare the current index with the current
! 2818: * separator */
! 2819: length = strlen (separators[iterator]);
! 2820: if (axl_memcmp (chunk + index, separators[iterator], length)) {
! 2821:
! 2822: /* copy the chunk found */
! 2823: result[count] = axl_new (char, index - previous_index + 1);
! 2824: memcpy (result[count], chunk + previous_index, index - previous_index);
! 2825:
! 2826: /* axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "item found (last) '%s' (index=%d != previous=%d", result[count], index, previous_index); */
! 2827:
! 2828: /* update items found */
! 2829: count++;
! 2830:
! 2831: /* update index to skip the item found */
! 2832: if (*(chunk + index + length) == 0) {
! 2833: /* in the case no more elements to read will be found */
! 2834: /* put an empty space at the end */
! 2835: result [count] = axl_new (char, 1);
! 2836:
! 2837: axl_free (separators);
! 2838: return result;
! 2839: }
! 2840:
! 2841: /* remember previous_index */
! 2842: index += length;
! 2843: previous_index = index;
! 2844: index--; /* make the last index to be captured the the -1 */
! 2845: break;
! 2846: }
! 2847: iterator++;
! 2848: }
! 2849:
! 2850: /* update the index to the next item */
! 2851: index++;
! 2852: }
! 2853:
! 2854: /* check for a last chunk */
! 2855: if (index != previous_index) {
! 2856: /* copy the chunk found */
! 2857: result[count] = axl_new (char, index - previous_index + 1);
! 2858: memcpy (result[count], chunk + previous_index, index - previous_index);
! 2859:
! 2860: /* axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "item found (last) '%s' (index=%d != previous=%d", result[count], index, previous_index); */
! 2861: }
! 2862:
! 2863:
! 2864: /* release memory */
! 2865: axl_free (separators);
! 2866:
! 2867: return result;
! 2868: }
! 2869:
! 2870: /**
! 2871: * @brief Allows to clean an split created by \ref axl_stream_split by
! 2872: * removing all items found to be empty strings.
! 2873: *
! 2874: * @param split The split to be updated by removing all empty string
! 2875: * items.
! 2876: *
! 2877: */
! 2878: void axl_stream_clean_split (char ** split)
! 2879: {
! 2880: int iterator;
! 2881: int iterator2;
! 2882: int iterator3;
! 2883:
! 2884: /* check input */
! 2885: axl_return_if_fail (split);
! 2886:
! 2887: /* remove empty strings */
! 2888: iterator = 0;
! 2889: while (split[iterator]) {
! 2890: if (strlen (split[iterator]) == 0) {
! 2891:
! 2892: /* clear position joint */
! 2893: axl_free (split[iterator]);
! 2894: split[iterator] = NULL;
! 2895:
! 2896: /* move strings */
! 2897: iterator3 = 0;
! 2898: iterator2 = iterator + 1;
! 2899: while (split[iterator2 + iterator3]) {
! 2900: /* move reference */
! 2901: split[iterator + iterator3] = split[iterator2 + iterator3];
! 2902:
! 2903: /* nullify */
! 2904: split[iterator2 + iterator3] = NULL;
! 2905:
! 2906: /* next position */
! 2907: iterator3++;
! 2908: } /* end while */
! 2909: continue;
! 2910: } /* end if */
! 2911:
! 2912: /* next iterator */
! 2913: iterator++;
! 2914: } /* end while */
! 2915:
! 2916: return;
! 2917: }
! 2918:
! 2919: /**
! 2920: * @brief Allows to implement the oposite operation of \ref
! 2921: * axl_stream_split, by joing all strings provided inside the array
! 2922: * (strings), using as separator the value provided.
! 2923: *
! 2924: * @param strings The set of strings to be joined.
! 2925: *
! 2926: * @param separator The separator to be used to join all strings
! 2927: * provided.
! 2928: *
! 2929: *
! 2930: * @return A newly allocated reference, that must be release using
! 2931: * \ref axl_free.
! 2932: *
! 2933: * NOTE: See also \ref axl_join.
! 2934: */
! 2935: char * axl_stream_join (char ** strings,
! 2936: const char * separator)
! 2937: {
! 2938: int length;
! 2939: int sep_length;
! 2940: int iterator;
! 2941: char * result;
! 2942: axl_bool next_sep;
! 2943:
! 2944: axl_return_val_if_fail (strings && strings[0], NULL);
! 2945: axl_return_val_if_fail (separator, NULL);
! 2946:
! 2947: /* get the amount of data to be allocated */
! 2948: length = 0;
! 2949: iterator = 0;
! 2950:
! 2951: /* for each value to be joined */
! 2952: while (strings [iterator]) {
! 2953: /* count the number of bytes for each string */
! 2954: length += strlen (strings[iterator]);
! 2955:
! 2956: /* next iterator */
! 2957: iterator++;
! 2958: } /* end while */
! 2959:
! 2960: /* check for the basic case */
! 2961: if (iterator == 1) {
! 2962: /* only one piece is contained in the set of strings
! 2963: * provided, so nothing can be joined */
! 2964: return axl_strdup (strings[0]);
! 2965: }
! 2966:
! 2967: /* add to the length the number of separatos to be added
! 2968: * (wihtout 1) and add a traling byte to terminate the
! 2969: * string */
! 2970: sep_length = strlen (separator);
! 2971: length += (sep_length * (iterator - 1)) + 1;
! 2972: result = axl_new (char, length);
! 2973:
! 2974: iterator = 0;
! 2975: next_sep = axl_false;
! 2976: length = 0;
! 2977:
! 2978: while (strings [iterator]) {
! 2979:
! 2980: /* copy the content */
! 2981: if (next_sep) {
! 2982: memcpy (result + length, separator, sep_length);
! 2983:
! 2984: /* update the length */
! 2985: length += sep_length;
! 2986: } else {
! 2987: memcpy (result + length, strings[iterator], strlen (strings[iterator]));
! 2988:
! 2989: /* update the length */
! 2990: length += strlen (strings[iterator]);
! 2991: } /* end if */
! 2992:
! 2993: /* check if next is separator */
! 2994: next_sep = ! next_sep;
! 2995:
! 2996: /* update the iterator only if next value to be
! 2997: * handled is a separator */
! 2998: if (next_sep)
! 2999: iterator++;
! 3000: } /* end while */
! 3001:
! 3002: /* return string created */
! 3003: return result;
! 3004: }
! 3005:
! 3006: /**
! 3007: * @brief Allows to concatenate the two given strings into a single
! 3008: * one.
! 3009: *
! 3010: * The function will concatenate both string into a newly allocated
! 3011: * string that is the result of taking chunk1 followed by chunk2.
! 3012: *
! 3013: * If the function receive a NULL value for one of the string
! 3014: * received, the result from this function will be the other
! 3015: * string. This is done to support a common basic case where the
! 3016: * string provided for one of the arguments is the one being used two
! 3017: * hold an iterative result. This variable usually is NULL.
! 3018: *
! 3019: * Once the string returned is no longer needed, \ref axl_free must be
! 3020: * used to deallocate the result.
! 3021: *
! 3022: * The function will use the strlen function to calculate current
! 3023: * chunk sizes to provide the result.
! 3024: *
! 3025: *
! 3026: * @param chunk1 The first chunk to be placed on the result.
! 3027: *
! 3028: * @param chunk2 The second chunk to be placed on the result.
! 3029: *
! 3030: * @return A newly allocated string, containing both strings, or NULL
! 3031: * if fails. The only way for this function to fail is to provide two
! 3032: * NULL references as incoming strings.
! 3033: *
! 3034: * NOTE: See also \ref axl_concat.
! 3035: */
! 3036: char * axl_stream_concat (const char * chunk1, const char * chunk2)
! 3037: {
! 3038: char * result;
! 3039: int len1;
! 3040: int len2;
! 3041:
! 3042: axl_return_val_if_fail ((chunk2 != NULL) || (chunk1 != NULL), NULL);
! 3043:
! 3044: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "concat called..");
! 3045:
! 3046: if (chunk1 == NULL) {
! 3047:
! 3048: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "concat called.., returning %s", chunk2);
! 3049:
! 3050: /* return the result */
! 3051: return axl_strdup (chunk2);
! 3052: }
! 3053:
! 3054: if (chunk2 == NULL) {
! 3055:
! 3056: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "concat called.., returning %s", chunk1);
! 3057:
! 3058: /* return the result */
! 3059: return axl_strdup (chunk1);
! 3060: }
! 3061:
! 3062: /* return the concatenation */
! 3063: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "concat called.., returning %s%s", chunk1, chunk2);
! 3064:
! 3065: /* alloc enough memory to hold both strings */
! 3066: len1 = strlen (chunk1);
! 3067: len2 = strlen (chunk2);
! 3068: result = axl_new (char, len1 + len2 + 1);
! 3069:
! 3070: /* copy the content */
! 3071: memcpy (result, chunk1, len1);
! 3072: memcpy (result + len1, chunk2, len2);
! 3073:
! 3074: /* return the string created */
! 3075: return result;
! 3076:
! 3077:
! 3078: }
! 3079:
! 3080:
! 3081: /**
! 3082: * @brief Returns current number of items inside the chunks reference
! 3083: * provided.
! 3084: *
! 3085: * @param chunks The chunks reference, which contains a list of
! 3086: * chunks.
! 3087: *
! 3088: * @return The number of chunks that the reference has or -1 if it
! 3089: * fails.
! 3090: */
! 3091: int axl_stream_strv_num (char ** chunks)
! 3092: {
! 3093: int iterator = 0;
! 3094:
! 3095: axl_return_val_if_fail (chunks, -1);
! 3096: /* release memory used by all elements inside the chunk */
! 3097: while (chunks[iterator] != 0) {
! 3098: iterator++;
! 3099: }
! 3100:
! 3101: /* return current number of chunks */
! 3102: return iterator;
! 3103:
! 3104: }
! 3105:
! 3106: /**
! 3107: * @brief Allows to release memory used by elements returned by \ref
! 3108: * axl_stream_split and other function that return a pointer to a char **.
! 3109: *
! 3110: * @param chunks The chunk to release.
! 3111: */
! 3112: void axl_stream_freev (char ** chunks)
! 3113: {
! 3114: int iterator = 0;
! 3115:
! 3116: axl_return_if_fail (chunks);
! 3117:
! 3118: /* release memory used by all elements inside the chunk */
! 3119: while (chunks[iterator] != 0) {
! 3120: axl_free (chunks[iterator]);
! 3121: iterator++;
! 3122: }
! 3123:
! 3124: /* now release the chunk inside */
! 3125: axl_free (chunks);
! 3126:
! 3127: /* nothing more to do */
! 3128: return;
! 3129: }
! 3130:
! 3131: /**
! 3132: * @internal
! 3133: *
! 3134: * @brief Internal function to support \ref axl_stream_to_upper and
! 3135: * \ref axl_stream_to_lower
! 3136: *
! 3137: * @param chunk The chunk to modify
! 3138: * @param desp Bits to increase.
! 3139: */
! 3140: void __axl_stream_common_to (char * chunk, axl_bool to_upper)
! 3141: {
! 3142: int iterator = 0;
! 3143:
! 3144: axl_return_if_fail (chunk);
! 3145:
! 3146: while (chunk[iterator] != 0) {
! 3147: /* change first ascii level */
! 3148: if (to_upper)
! 3149: chunk[iterator] = toupper (chunk[iterator]);
! 3150: else
! 3151: chunk[iterator] = tolower (chunk[iterator]);
! 3152:
! 3153: /* update iterators */
! 3154: iterator++;
! 3155: }
! 3156:
! 3157: return;
! 3158: }
! 3159:
! 3160: /**
! 3161: * @brief Makes the provided string to be converted to upper case
! 3162: * letters.
! 3163: *
! 3164: * The function will modify the address provided. If you want to get a
! 3165: * upper case copy for a particular string, copy it first, by using
! 3166: * \ref axl_strdup, and then use this function.
! 3167: *
! 3168: * The function, for convenience, will also return the string
! 3169: * reference received, already modified. This could be used while
! 3170: * using this function to convert parameters that are expected by
! 3171: * other functions.
! 3172: *
! 3173: * @param chunk The chunk to upper case.
! 3174: */
! 3175: char * axl_stream_to_upper (char * chunk)
! 3176: {
! 3177: __axl_stream_common_to (chunk, axl_true);
! 3178: return chunk;
! 3179: }
! 3180:
! 3181: /**
! 3182: * @brief Allows to convert the provided string into lower cases
! 3183: * letter.
! 3184: *
! 3185: * The function, for convenience, will also return the string
! 3186: * reference received, already modified. This could be used while
! 3187: * using this function to convert parameters that are expected by
! 3188: * other functions.
! 3189: *
! 3190: * @param chunk The chunk to lower case.
! 3191: */
! 3192: char * axl_stream_to_lower (char * chunk)
! 3193: {
! 3194: __axl_stream_common_to (chunk, axl_false);
! 3195: return chunk;
! 3196: }
! 3197:
! 3198: /**
! 3199: * @brief Allows to perform a to upper operation, like \ref
! 3200: * axl_stream_to_upper, but returning an new allocated reference.
! 3201: *
! 3202: * @param chunk The string reference to upper.
! 3203: *
! 3204: * @return A new reference allocated containing the result or NULL if
! 3205: * it fails.
! 3206: */
! 3207: char * axl_stream_to_upper_copy (const char * chunk)
! 3208: {
! 3209: char * result;
! 3210:
! 3211: /* perform some environmental checks */
! 3212: axl_return_val_if_fail (chunk, NULL);
! 3213:
! 3214: /* make a copy */
! 3215: result = axl_strdup (chunk);
! 3216:
! 3217: /* upper it */
! 3218: __axl_stream_common_to (result, axl_true);
! 3219:
! 3220: /* return the result */
! 3221: return result;
! 3222: }
! 3223:
! 3224:
! 3225: /**
! 3226: * @brief Allows to perform a to lower operation, like \ref
! 3227: * axl_stream_to_upper, but returning an new allocated reference.
! 3228: *
! 3229: * @param chunk The string reference to lower.
! 3230: *
! 3231: * @return A new reference allocated containing the result or NULL if
! 3232: * it fails.
! 3233: */
! 3234: char * axl_stream_to_lower_copy (const char * chunk)
! 3235: {
! 3236: char * result;
! 3237:
! 3238: /* perform some environmental checks */
! 3239: axl_return_val_if_fail (chunk, NULL);
! 3240:
! 3241: /* make a copy */
! 3242: result = axl_strdup (chunk);
! 3243:
! 3244: /* lower it */
! 3245: __axl_stream_common_to (result, axl_false);
! 3246:
! 3247: /* return the result */
! 3248: return result;
! 3249: }
! 3250:
! 3251: /**
! 3252: * @brief Allows to compare two strings provided, s1 and s1 to be
! 3253: * equal.
! 3254: *
! 3255: * In the case both are equal, \ref axl_true is returned. Otherwise \ref
! 3256: * axl_false. The function compares that both are equal not only by making
! 3257: * the first to be contained inside the second string. The check also
! 3258: * ensures that "test" isn't equal to "test1".
! 3259: *
! 3260: * @param string First string to check.
! 3261: *
! 3262: * @param string2 Second string to check.
! 3263: *
! 3264: * @return \ref axl_true if both string are equal, otherwise \ref axl_false is
! 3265: * returned.
! 3266: */
! 3267: axl_bool axl_cmp (const char * string, const char * string2)
! 3268: {
! 3269: int iterator = 0;
! 3270:
! 3271: if (string == NULL)
! 3272: return axl_false;
! 3273: if (string2 == NULL)
! 3274: return axl_false;
! 3275:
! 3276: /* for each item inside the iterator */
! 3277: while (string [iterator] != 0 && string2 [iterator] != 0) {
! 3278:
! 3279: /* check the content */
! 3280: if (string [iterator] != string2 [iterator])
! 3281: return axl_false;
! 3282:
! 3283: /* update the iterator */
! 3284: iterator++;
! 3285:
! 3286: } /* end while */
! 3287:
! 3288: /* check that both string ends at the same point */
! 3289: if (string [iterator] != 0 ||
! 3290: string2 [iterator] != 0)
! 3291: return axl_false;
! 3292:
! 3293: return axl_true;
! 3294: }
! 3295:
! 3296: axl_bool axl_casecmp (const char * string, const char * string2)
! 3297: {
! 3298: int length;
! 3299:
! 3300: if (string == NULL)
! 3301: return axl_false;
! 3302: if (string2 == NULL)
! 3303: return axl_false;
! 3304:
! 3305: /* get length associated to first string */
! 3306: length = strlen (string);
! 3307: if (length != strlen (string2))
! 3308: return axl_false;
! 3309:
! 3310: /* now check both lengths */
! 3311: return axl_stream_casecmp (string, string2, length);
! 3312:
! 3313: }
! 3314:
! 3315:
! 3316: /**
! 3317: * @brief Allows to check if both strings provided are equal on its
! 3318: * initial size bytes.
! 3319: *
! 3320: * This function is more efficient than common memcmp because it
! 3321: * doesn't perform the additional work to figure out which are the
! 3322: * bytes that differ both strings.
! 3323: *
! 3324: * @param string The string to check.
! 3325: *
! 3326: * @param string2 The second string to check.
! 3327: *
! 3328: * @param size The size to check for both strings to be equal.
! 3329: *
! 3330: * @return \ref axl_true if the both strings are equal for its initial
! 3331: * size bytes or \ref axl_false if not.
! 3332: */
! 3333: axl_bool axl_memcmp (const char * string, const char * string2, int size)
! 3334: {
! 3335: int iterator = 0;
! 3336:
! 3337: _memcmp(iterator,string,string2,size);
! 3338: }
! 3339:
! 3340: /**
! 3341: *
! 3342: * @brief Perform a memory copy from the string provided.
! 3343: *
! 3344: * @param string The string to copy.
! 3345: *
! 3346: * @return A newly allocated value or NULL if it fails. The value
! 3347: * returned, must be deallocated using \ref axl_free.
! 3348: */
! 3349: char * axl_strdup (const char * string)
! 3350: {
! 3351: return (string != NULL) ? (char *) axl_stream_strdup ((char *) string) : NULL;
! 3352: }
! 3353:
! 3354: /**
! 3355: * @internal Function that handles decoding operations when decode
! 3356: * functions are defined.
! 3357: *
! 3358: * @param stream The stream where the decode operation will be
! 3359: * performed.
! 3360: *
! 3361: * @param error Optional \ref axlError reference where errors will be
! 3362: * reported.
! 3363: *
! 3364: * @return axl_true if the operation was completed.
! 3365: */
! 3366: axl_bool axl_stream_decode (axlStream * stream,
! 3367: char * output,
! 3368: int output_max_size,
! 3369: int * output_decoded,
! 3370: int * op_result,
! 3371: axlError ** error)
! 3372: {
! 3373:
! 3374: int result;
! 3375: int size;
! 3376:
! 3377: /* clear op_result if defined */
! 3378: if (op_result)
! 3379: *op_result = 0;
! 3380:
! 3381: /* decode content from the stream directly */
! 3382: result = stream->decode_f (
! 3383: /* source */
! 3384: stream->decode_temp,
! 3385: /* source size */
! 3386: stream->decode_temp_last,
! 3387: /* source encoding: encode used by the source
! 3388: * content provided */
! 3389: stream->source_encoding,
! 3390: /* output of content decoded and its size */
! 3391: output,
! 3392: /* output max size */
! 3393: output_max_size,
! 3394: /* output decoded */
! 3395: output_decoded,
! 3396: /* source remaining */
! 3397: &(stream->decode_temp_remain),
! 3398: /* user defined data */
! 3399: stream->decode_f_data);
! 3400:
! 3401: /* set op result if defined */
! 3402: if (op_result)
! 3403: *op_result = result;
! 3404:
! 3405: /* check result */
! 3406: if (result == 0) {
! 3407: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "after decode operation result=%d, output_decoded (new buffer size)=%d (from %d original bytes)",
! 3408: result, output_decoded ? *output_decoded : 0, stream->decode_temp_last);
! 3409: axl_error_new (-1, "found internal failure at decode operation, unable to complete entity parsing",
! 3410: stream, error);
! 3411: return axl_false;
! 3412: } /* end if */
! 3413:
! 3414: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "after decode operation result=%d, output_decoded (new buffer size)=%d",
! 3415: result, output_decoded ? *output_decoded : 0);
! 3416:
! 3417: /* update axl stream internal state */
! 3418: if (result == 1) {
! 3419: /* then the conversión was complete a no data
! 3420: * is pending the the temporal decode
! 3421: * buffer */
! 3422: stream->decode_temp_index = 0;
! 3423: stream->decode_temp_last = 0;
! 3424: } else if (result == 2) {
! 3425: /* not enough space was found at the destination
! 3426: * buffer, pack content at the decode buffer at the
! 3427: * next operation */
! 3428: size = stream->decode_temp_last - stream->decode_temp_remain;
! 3429: if (size <= 0) {
! 3430: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL,
! 3431: "found decode function return 2 (signaling pending data to be decoded) but last - remain yields to %d",
! 3432: size);
! 3433: axl_error_new (-1, "found decode function return 2 (signaling pending data to be decoded) but last - remain yields to 0 or less", NULL, error);
! 3434: return axl_false;
! 3435: } /* end if */
! 3436:
! 3437: /* moving data */
! 3438: while (stream->decode_temp_index < size) {
! 3439: stream->decode_temp[stream->decode_temp_index] = stream->decode_temp[stream->decode_temp_remain + stream->decode_temp_index];
! 3440: stream->decode_temp_index++;
! 3441: } /* end while */
! 3442:
! 3443: /* now reset */
! 3444: stream->decode_temp_index = 0;
! 3445: stream->decode_temp_last = size;
! 3446:
! 3447: /* reset to 1 since we have moved content to
! 3448: * the begin of the buffer */
! 3449: result = 1;
! 3450: } /* end if */
! 3451:
! 3452: return (result == 1);
! 3453: }
! 3454:
! 3455: /**
! 3456: * @internal Function used by the axl stream module to call to check
! 3457: * function defined.
! 3458: *
! 3459: * @return axl_true if the check was fine, otherwise axl_false is returned.
! 3460: */
! 3461: axl_bool axl_stream_content_check (axlStream * stream, const char * content, int content_length, axlError ** error)
! 3462: {
! 3463: if (stream == NULL || content == NULL) {
! 3464: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "content check function failed because null reference was received.");
! 3465: axl_error_new (-1, "content check function failed because null reference was received.", stream, error);
! 3466: return axl_false;
! 3467: } /* end if */
! 3468:
! 3469: /* return appropiate value */
! 3470: if (stream->check_f (content, content_length, stream->source_encoding, stream->check_f_data, error) == 1)
! 3471: return axl_true;
! 3472:
! 3473: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "content check function have failed");
! 3474:
! 3475: /* check if error reference was defined */
! 3476: return axl_false;
! 3477: }
! 3478:
! 3479:
! 3480: /**
! 3481: * @internal Allows to configure a decode functions to be used to
! 3482: * translate content read into utf-8.
! 3483: *
! 3484: * @param stream The stream to be configured.
! 3485: *
! 3486: * @param source_encoding Original source encode.
! 3487: *
! 3488: * @param decode_f The function to be called to translate/check
! 3489: * content read into utf-8. If a null value is provided the decode
! 3490: * function is removed.
! 3491: *
! 3492: * @param error The reference where errors will be reported.
! 3493: *
! 3494: * @return axl_true if the function setup decode handler properly
! 3495: * otherwise axl_false is returned.
! 3496: */
! 3497: axl_bool axl_stream_setup_decode (axlStream * stream,
! 3498: const char * source_encoding,
! 3499: axlStreamDecode decode_f,
! 3500: axlPointer user_data,
! 3501: axlError ** error)
! 3502: {
! 3503: axl_return_val_if_fail (stream, axl_false);
! 3504:
! 3505: /* do not check if the decode_f function is NULL (it's a valid
! 3506: * case) */
! 3507: stream->decode_f = decode_f;
! 3508: stream->decode_f_data = user_data;
! 3509:
! 3510: /* store source encoding */
! 3511: if (source_encoding != NULL)
! 3512: stream->source_encoding = axl_strdup (source_encoding);
! 3513:
! 3514: /* call to check and decode if required bufferede content */
! 3515: if (stream->decode_f) {
! 3516: /* init decode buffer */
! 3517: stream->decode_temp_size = (stream->buffer_size * 2) + 1;
! 3518: stream->decode_temp = axl_new (char, stream->decode_temp_size);
! 3519:
! 3520: /* move content into decode temporal buffer */
! 3521: memcpy (stream->decode_temp,
! 3522: stream->stream + stream->stream_index,
! 3523: stream->stream_size - stream->stream_index);
! 3524: stream->decode_temp_index = 0;
! 3525: stream->decode_temp_last = stream->stream_size - stream->stream_index;
! 3526: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "procesing %d bytes from decode buffer (total size: %d, current index: 0)",
! 3527: stream->decode_temp_last, stream->decode_temp_size);
! 3528:
! 3529: /* call to decode content */
! 3530: if (! axl_stream_decode (stream,
! 3531: /* output */
! 3532: (stream->stream + stream->stream_index),
! 3533: /* output max size */
! 3534: stream->buffer_size - stream->stream_index,
! 3535: /* output decoded */
! 3536: &(stream->stream_size),
! 3537: /* do not define op result */
! 3538: NULL,
! 3539: error))
! 3540: return axl_false;
! 3541:
! 3542: /* add to the stream size the current index */
! 3543: stream->stream_size += stream->stream_index;
! 3544:
! 3545: } /* end if */
! 3546:
! 3547: /* return result */
! 3548: return axl_true;
! 3549: }
! 3550:
! 3551: /**
! 3552: * @brief Function that allows to configure a handler that is executed
! 3553: * to check content read into the axl stream buffer. See \ref
! 3554: * axlStreamContentCheck for more information.
! 3555: *
! 3556: * @param stream The stream that is going to be configured.
! 3557: *
! 3558: * @param source_encoding The source encoding detected.
! 3559: *
! 3560: * @param check The function that implements the check.
! 3561: *
! 3562: * @param user_data User defined data to be passed to the check
! 3563: * function.
! 3564: *
! 3565: * @param error Optional \ref axlError reference where errors will be
! 3566: * reported.
! 3567: *
! 3568: * @return The function returns axl_true if the cheker was installed and
! 3569: * first execution was completed.
! 3570: */
! 3571: axl_bool axl_stream_setup_check (axlStream * stream,
! 3572: const char * source_encoding,
! 3573: axlStreamContentCheck check,
! 3574: axlPointer user_data,
! 3575: axlError ** error)
! 3576: {
! 3577: axl_return_val_if_fail (stream, axl_false);
! 3578:
! 3579: /* do not check if the decode_f function is NULL (it's a valid
! 3580: * case) */
! 3581: stream->check_f = check;
! 3582: stream->check_f_data = user_data;
! 3583:
! 3584: /* store source encoding */
! 3585: if (source_encoding != NULL)
! 3586: stream->source_encoding = axl_strdup (source_encoding);
! 3587:
! 3588: if (stream->check_f) {
! 3589: /* call to check */
! 3590: if (! axl_stream_content_check (stream, stream->stream + stream->stream_index, stream->stream_size - stream->stream_index, error)) {
! 3591: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "content check function have failed, looks like there is a problem with content");
! 3592: return axl_false;
! 3593: } /* end if */
! 3594: } /* end if */
! 3595:
! 3596: return axl_true;
! 3597: }
! 3598:
! 3599: /* @} */
! 3600:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>