File:  [ELWIX - Embedded LightWeight unIX -] / gpl / axl / src / axl_stream.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Fri Feb 17 12:50:03 2012 UTC (12 years, 4 months ago) by misho
Branches: axl, MAIN
CVS tags: HEAD, AXL0_6_7
version 0.6.7

    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->global_index     += length;
 1510: 			}
 1511: 			stream->stream_index             += index;
 1512: 			stream->global_index             += index;
 1513: 			stream->previous_inspect          = 0;
 1514: 			
 1515: 			
 1516: 			/* return allocated result */
 1517: 			if (! stream->result_size)
 1518: 				return stream->last_chunk;
 1519: 
 1520: 			/* return reference result */
 1521: 			return string;
 1522: 		} /* end if */
 1523: 		
 1524: 		/* it seems that the chunk wasn't found */
 1525: 		index++;
 1526: 
 1527: 	}while (axl_true);
 1528: 
 1529: 	/* return a NULL chunk. */
 1530: 	return NULL;	
 1531: }
 1532: 
 1533: /** 
 1534:  * @brief Allows to perform the same operation like \ref
 1535:  * axl_stream_get_untilv but providing an already initialized and
 1536:  * opened std arg.
 1537:  *
 1538:  * This function is in fact, used by \ref axl_stream_get_untilv.
 1539:  * 
 1540:  * @param stream The stream where the operation will be peformed.
 1541:  * 
 1542:  * @param valid_chars The valid chars set to be used while reading
 1543:  * data.
 1544:  *
 1545:  * @param chunk_matched An optional value where the matched chunk will
 1546:  * be reported.
 1547:  *
 1548:  * @param accept_terminator Configure if terminator read should be
 1549:  * accepted or only the chunk read.
 1550:  *
 1551:  * @param result_size Allows to notify the caller with the chunk size
 1552:  * that is being returned by the function.
 1553:  *
 1554:  * @param chunk_num How many terminators are configured.
 1555:  *
 1556:  * @param args The list of terminators.
 1557:  * 
 1558:  * @return The chunk read or NULL if fails.
 1559:  */
 1560: char      * axl_stream_get_untilv      (axlStream * stream, 
 1561: 					char      * valid_chars, 
 1562: 					int       * chunk_matched,
 1563: 					axl_bool    accept_terminator,
 1564: 					int       * result_size,
 1565: 					int         chunk_num, 
 1566: 					va_list args)
 1567: {
 1568: 	char * result;
 1569: 
 1570: 
 1571: 	/* check max inspected chunks */
 1572: 	if (chunk_num > MAX_INSPECTED_CHUNKS) {
 1573: 		__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",
 1574: 			   MAX_INSPECTED_CHUNKS, stream->chunk_num);
 1575: 		return NULL;
 1576: 	}
 1577: 
 1578: 	/* configure variables for the operation */
 1579: 	stream->valid_chars       = valid_chars;
 1580: 	stream->accept_terminator = accept_terminator;
 1581: 	stream->result_size       = (result_size != NULL);
 1582: 	stream->chunk_num         = chunk_num;
 1583: 
 1584: 	/* call to current implementation */
 1585: 	result = __axl_stream_get_untilv_wide (stream, args);
 1586: 
 1587: 	/* check for returning references */
 1588: 	if (result_size != NULL)
 1589: 		*result_size   = stream->result_size;
 1590: 	if (chunk_matched != NULL)
 1591: 		*chunk_matched = stream->chunk_matched;
 1592: 
 1593: 	/* return string matched */
 1594: 	return result;
 1595: }
 1596: 
 1597: 
 1598: /** 
 1599:  * @brief Returns current index status for the given stream.
 1600:  *
 1601:  * This function could be used to get current index being read for the
 1602:  * stream received.
 1603:  * 
 1604:  * @param stream The stream where the index will be reported.
 1605:  * 
 1606:  * @return The index or -1 if fails.
 1607:  */
 1608: int         axl_stream_get_index       (axlStream * stream)
 1609: {
 1610: 	axl_return_val_if_fail (stream, -1);
 1611: 
 1612: 	return stream->stream_index;
 1613: }
 1614: 
 1615: /** 
 1616:  * @brief Returns current global index for the device being streamed.
 1617:  * 
 1618:  * @param stream The stream where the data is being requested.
 1619:  * 
 1620:  * @return The index or -1 if fails.
 1621:  */
 1622: int         axl_stream_get_global_index (axlStream * stream)
 1623: {
 1624: 	axl_return_val_if_fail (stream, -1);
 1625: 
 1626: 	return stream->global_index;
 1627: }
 1628: 
 1629: /** 
 1630:  * @brief Allows to get current stream size.
 1631:  * 
 1632:  * @param stream The stream where the stream size will be returned.
 1633:  * 
 1634:  * @return The stream size or -1 if fails. 
 1635:  */
 1636: int         axl_stream_get_size        (axlStream * stream)
 1637: {
 1638: 	axl_return_val_if_fail (stream, -1);
 1639: 
 1640: 	return stream->stream_size;
 1641: }
 1642: 
 1643: /** 
 1644:  * @brief Allows to get current status of the given stream, taking the
 1645:  * current index, getting an amount of <b>count</b> bytes before and
 1646:  * after the given index.
 1647:  *
 1648:  * This function is mainly used to get a piece of the stream at the
 1649:  * given position while reporting errors. This allows to show the
 1650:  * piece of xml that is failing.
 1651:  *
 1652:  * The string return must not be deallocated. Value returned is
 1653:  * actually managed by the stream object associated.
 1654:  * 
 1655:  * @param stream The stream where the near to operation will be performed.
 1656:  * @param count The amount of bytes to take.
 1657:  * 
 1658:  * @return A string that is taken counting bytes with provided
 1659:  * <b>count</b> value starting from the index. Stream provided must be
 1660:  * not NULL and count value must be greater than 0. 
 1661:  */
 1662: const char  * axl_stream_get_near_to     (axlStream * stream, int count)
 1663: {
 1664: 	int first_index;
 1665: 	int last_index;
 1666: 
 1667: 	axl_return_val_if_fail (stream, NULL);
 1668: 	axl_return_val_if_fail (count > 0, NULL);
 1669: 	
 1670: 	/* get first index */
 1671: 	if ((stream->stream_index - count) <= 0)
 1672: 		first_index = 0;
 1673: 	else
 1674: 		first_index = stream->stream_index - count;
 1675: 
 1676: 	/* get last index */
 1677: 	if ((stream->stream_index + count) >= (stream->stream_size - 1) )
 1678: 		last_index  = (stream->stream_size) - first_index;
 1679: 	else
 1680: 		last_index  = (stream->stream_index + count) - first_index;
 1681: 
 1682: 	/* release previous near to chunk */
 1683: 	if (stream->last_near_to != NULL)
 1684: 		axl_free (stream->last_near_to);
 1685: 
 1686: 	stream->last_near_to = axl_new (char, last_index + 1);
 1687: 	memcpy (stream->last_near_to, stream->stream + first_index, last_index);
 1688: 
 1689: 	/* return current near to operation */
 1690: 	return stream->last_near_to;
 1691: 	
 1692: }
 1693: 
 1694: /** 
 1695:  * @brief Allows to get the following <b>count</b> bytes read from the
 1696:  * stream.
 1697:  * 
 1698:  * @param stream The stream where the operation will be performed.
 1699:  * @param count How many bytes to get from the stream.
 1700:  * 
 1701:  * @return A string referece, containing the first <b>count</b> bytes
 1702:  * or NULL if fails. Reference returned shouldn't be deallocated.
 1703:  */
 1704: const char  * axl_stream_get_following   (axlStream * stream, int count)
 1705: {
 1706: 	axl_return_val_if_fail (stream, NULL);
 1707: 
 1708: 	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "getting next characters from stream: index=%d size=%d",
 1709: 		   stream->stream_index, stream->stream_size);
 1710: 
 1711: 	/* check index */
 1712: 	if (stream->stream_index >= stream->stream_size)
 1713: 		return NULL;
 1714: 
 1715: 	if ((count + stream->stream_index) > stream->stream_size) {
 1716: 		count = stream->stream_size - stream->stream_index;
 1717: 	}
 1718: 
 1719: 	/* free previously allocated memory */
 1720: 	if (stream->last_get_following != NULL)
 1721: 		axl_free (stream->last_get_following);
 1722: 
 1723: 	/* copy stream content */
 1724: 	stream->last_get_following = axl_new (char, count + 1);
 1725: 	memcpy (stream->last_get_following, stream->stream + stream->stream_index, count);
 1726: 
 1727: 	/* return reference created */
 1728: 	return stream->last_get_following;
 1729: }
 1730: 
 1731: typedef struct _AxlStreamAssociatedData {
 1732: 	axlPointer       data;
 1733: 	axlDestroyFunc   destroy_func;
 1734: 	axl_bool         free_on_finish;
 1735: }AxlStreamAssociatedData;
 1736: 
 1737: 
 1738: /** 
 1739:  * @internal
 1740:  * 
 1741:  * Internal deallocation function to reclaim memory used by the \ref
 1742:  * AxlStreamAssociatedData.
 1743:  * 
 1744:  * @param data The data to be deallocated.
 1745:  */
 1746: void __stream_associated_data_free (AxlStreamAssociatedData * data)
 1747: {
 1748: 	/* do nothing if NULL is received */
 1749: 	if (data == NULL)
 1750: 		return;
 1751: 
 1752: 	/* destroy associated data used provided destroy function */
 1753: 	if (data->destroy_func != NULL && data->data != NULL)
 1754: 		data->destroy_func (data->data);
 1755: 
 1756: 	/* now free the node it self */
 1757: 	axl_free (data);
 1758: 
 1759: 	return;
 1760: }
 1761: 
 1762: /** 
 1763:  * @brief Associates the given \ref axlPointer with the given stream to be
 1764:  * life-time dependant.
 1765:  *
 1766:  * While performing the XML parsing, errors will be produced. This
 1767:  * function ensures that the axlDoc document will be released if the
 1768:  * stream is also released.
 1769:  *
 1770:  * This not only reduces the possibility to produce a memory leak also
 1771:  * allows to write less code.
 1772:  *
 1773:  * Once the stream is not useful and it is required to be released,
 1774:  * but not doing so with the \ref axlDoc instance, a call to \ref
 1775:  * axl_stream_unlink is also required.
 1776:  * 
 1777:  * @param stream The axlStream where the document will be linked to.
 1778:  *
 1779:  * @param element The element to link (may a axlDoc or a axlDtd).
 1780:  *
 1781:  * @param func The function to call once the stream is released.
 1782:  */
 1783: void        axl_stream_link            (axlStream  *   stream,
 1784: 					axlPointer     element,
 1785: 					axlDestroyFunc func)
 1786: {
 1787: 	/* call to base implementation */
 1788: 	axl_stream_link_full (stream, element, func, axl_false);
 1789: 
 1790: 	return;
 1791: }
 1792: 
 1793: /** 
 1794:  * @brief Allows to associate data references with a destroy function,
 1795:  * like \ref axl_stream_link, but ensuring the object reference will
 1796:  * be released once finished the axl stream, no mather if the
 1797:  * application code calls to \ref axl_stream_unlink.
 1798:  * 
 1799:  * @param stream The axlStream where the document will be linked to.
 1800:  *
 1801:  * @param element The element to link (may a axlDoc or a axlDtd).
 1802:  *
 1803:  *
 1804:  * @param func The function to call once the stream is released.
 1805:  *
 1806:  * @param free_on_finish axl_true to make the reference to be released on
 1807:  * \ref axlStream deallocation. Providing \ref axl_false to this value is
 1808:  * equivalent to call to \ref axl_stream_link directly.
 1809:  */
 1810: void       axl_stream_link_full     (axlStream  *   stream,
 1811: 				     axlPointer     element,
 1812: 				     axlDestroyFunc func,
 1813: 				     axl_bool       free_on_finish)
 1814: {
 1815: 	AxlStreamAssociatedData * data;
 1816: 	axl_return_if_fail (stream);
 1817: 	axl_return_if_fail (element);
 1818: 	axl_return_if_fail (func);
 1819: 	
 1820: 	/* that's all */
 1821: 	if (stream->elements_linked == NULL)
 1822: 		stream->elements_linked = axl_list_new (axl_list_always_return_1, 
 1823: 							(axlDestroyFunc) __stream_associated_data_free);
 1824: 
 1825: 	/* create the data to be stored */
 1826: 	data                 = axl_new (AxlStreamAssociatedData, 1);
 1827: 	data->data           = element;
 1828: 	data->destroy_func   = func;
 1829: 	data->free_on_finish = free_on_finish;
 1830: 
 1831: 	/* add the item to be destroy once the stream is unrefered */
 1832: 	axl_list_add (stream->elements_linked, data);
 1833: 
 1834: 	return;
 1835: }
 1836: 
 1837: /** 
 1838:  * @brief Unlinks the associated \ref axlDoc instance.
 1839:  * 
 1840:  * @param stream The stream where the operation will be performed.
 1841:  */
 1842: void axl_stream_unlink (axlStream * stream)
 1843: {
 1844: 	int                       iterator;
 1845: 	AxlStreamAssociatedData * data;
 1846: 
 1847: 	axl_return_if_fail (stream);
 1848: 	
 1849: 	/* clear document association */
 1850: 	iterator = 0;
 1851: 	while (iterator < axl_list_length (stream->elements_linked)) {
 1852: 		/* get a referece to the node to destroy */
 1853: 		data = axl_list_get_nth (stream->elements_linked, iterator);
 1854: 		/* clear it */
 1855: 		if (! data->free_on_finish) {
 1856: 			data->data         = NULL;
 1857: 			data->destroy_func = NULL;
 1858: 		}
 1859: 		iterator++;
 1860: 	}
 1861: 	return;
 1862: }
 1863: 
 1864: /** 
 1865:  * @brief Allows to deallocate memory used by the \ref axlStream
 1866:  * received.
 1867:  * 
 1868:  * @param stream The stream to be deallocated.
 1869:  */
 1870: void axl_stream_free (axlStream * stream)
 1871: {
 1872: 	axl_return_if_fail (stream);
 1873: 
 1874: 	/* release memory */
 1875: 	axl_free (stream->stream);
 1876: 
 1877: 	/* release associated document is defined. */
 1878: 	if (stream->elements_linked) 
 1879: 		axl_list_free (stream->elements_linked);
 1880: 
 1881: 	/* releaset last chunk */
 1882: 	if (stream->last_chunk != NULL)
 1883: 		axl_free (stream->last_chunk);
 1884: 
 1885: 	/* releaset last near to */
 1886: 	if (stream->last_near_to != NULL)
 1887: 		axl_free (stream->last_near_to);
 1888: 
 1889: 	/* releaset last get following */
 1890: 	if (stream->last_get_following != NULL)
 1891: 		axl_free (stream->last_get_following);
 1892: 
 1893: 	if (stream->fd > 0) {
 1894: 		/* close file descriptor if defined */
 1895: 		close (stream->fd);
 1896: 	}
 1897: 
 1898: 	/* free memory allocated for chunk matching */
 1899: 	axl_free (stream->chunks);
 1900: 
 1901: 	/* free lengths */
 1902: 	axl_free (stream->lengths);
 1903: 
 1904: 	/* free temporal buffer */
 1905: 	axl_free (stream->temp);
 1906: 	axl_free (stream->decode_temp);
 1907: 	axl_free (stream->source_encoding);
 1908: 
 1909: 	/* release memory allocated by the stream received. */
 1910: 	axl_free (stream);
 1911: 
 1912: 	return;
 1913: }
 1914: 
 1915: 
 1916: /** 
 1917:  * @brief Allows to check if the given chunk is a white space in the
 1918:  * same of the XML 1.0 Third edition.
 1919:  *
 1920:  * The XML standard understand the "white space", also reffered as S,
 1921:  * as the following characters: \\x20 (the white space itself), \\n, \\r
 1922:  * and \\t.
 1923:  *
 1924:  * This function allows to check if the given chunk contains a white
 1925:  * space, in the previous sense.
 1926:  * 
 1927:  * @param chunk The chunk to check
 1928:  * 
 1929:  * @return axl_true if the chunk contains a white space or axl_false
 1930:  * if not.
 1931:  */
 1932: axl_bool        axl_stream_is_white_space  (char * chunk)
 1933: {
 1934: 	/* do not complain about receive a null refernce chunk */
 1935: 	if (chunk == NULL)
 1936: 		return axl_false;
 1937: 	
 1938: 	if (chunk[0] == ' ')
 1939: 		return axl_true;
 1940: 	if (chunk[0] == '\n')
 1941: 		return axl_true;
 1942: 	if (chunk[0] == '\t')
 1943: 		return axl_true;
 1944: 	if (chunk[0] == '\r')
 1945: 		return axl_true;
 1946: 
 1947: 	/* no white space was found */
 1948: 	return axl_false;
 1949: }
 1950: 
 1951: /** 
 1952:  * @brief Support function which consumes white spaces in the W3C
 1953:  * sense.
 1954:  * 
 1955:  * @param stream The stream where the operation will be performed.
 1956:  * 
 1957:  * @return axl_true if more white spaces could be consumed, axl_false
 1958:  * if not.
 1959:  */
 1960: void axl_stream_consume_white_spaces (axlStream * stream)
 1961: {
 1962: 	/* get how many bytes remains to be read */
 1963: 	int remains = stream->stream_size - stream->stream_index;
 1964: 
 1965: 	while (axl_true) {
 1966: 		/* decrase the number of bytes remaining to be read
 1967: 		 * and check if it is zero or less than zero to
 1968: 		 * prebuffer. NOTE: remains could be 1 (remains one
 1969: 		 * byte) making it possible to consume one byte
 1970: 		 * more */
 1971: 		remains--;
 1972: 		if (remains < 0) {
 1973: 			/* we fall outside the stream, so a prebuffer
 1974: 			 * operation is required */
 1975: 			if (! axl_stream_prebuffer (stream))
 1976: 				return;
 1977: 			
 1978: 			/* update remains value but this time removing
 1979: 			 * one unit (the unit to be consumed at the
 1980: 			 * next sentence) */
 1981: 			remains = stream->stream_size - stream->stream_index - 1;
 1982:                 }
 1983: 		
 1984: 		/* check for a white space */
 1985: 		if ((stream->stream[stream->stream_index] == ' ')  ||
 1986: 		    (stream->stream[stream->stream_index] == '\n') ||
 1987: 		    (stream->stream[stream->stream_index] == '\t') ||
 1988: 		    (stream->stream[stream->stream_index] == '\r')) {
 1989: 
 1990: 			/* update internal indexes */
 1991: 			stream->stream_index++;
 1992: 			stream->global_index++;
 1993: 			stream->previous_inspect  = 0;
 1994: 		}else {
 1995: 			/* return */
 1996: 			return;
 1997: 		}
 1998: 		
 1999: 	} /* end while */
 2000: 
 2001: 	return;
 2002: }
 2003: 
 2004: 
 2005: 
 2006: /** 
 2007:  * @internal
 2008:  * @brief Allows to compare two strings pointed by 
 2009:  * 
 2010:  * @param chunk1 The string to be compared.
 2011:  *
 2012:  * @param chunk2 The second string to be compared.
 2013:  *
 2014:  * @param size The amount of bytes to be compared for the two incoming
 2015:  * values.
 2016:  * 
 2017:  * @return axl_true if both string are equal, axl_false if not. If
 2018:  * some value provided is NULL or the size to compare is not greater
 2019:  * than 0 the function will return axl_false directly.
 2020:  */
 2021: axl_bool        axl_stream_cmp             (const char * chunk1, const char * chunk2, int size)
 2022: {
 2023: 	/* perform some environmental condition checking */
 2024: 	if (chunk1 == NULL)
 2025: 		return axl_false;
 2026: 	if (chunk2 == NULL)
 2027: 		return axl_false;
 2028: 	if (size < 0)
 2029: 		return axl_false;
 2030: 	
 2031: 	/* report current comparation status */
 2032: 	if ((chunk1[0] == chunk2[0])) {
 2033: 		if ((size == 1) ||
 2034: 		    (axl_memcmp (chunk1 + 1, chunk2 + 1, size -1))) {
 2035: 			return axl_true;
 2036: 		} /* end if */
 2037: 	} /* end if */
 2038: 
 2039: 	return axl_false;
 2040: }
 2041: 
 2042: /** 
 2043:  * @brief Provides the same function like axl_stream_cmp but
 2044:  * ignoring the case of the characters (case insensitive manner).
 2045:  * 
 2046:  * @param chunk1 The string to be compared.
 2047:  *
 2048:  * @param chunk2 The second string to be compared.
 2049:  *
 2050:  * @param size The amount of bytes to be compared for the two incoming
 2051:  * values.
 2052:  * 
 2053:  * @return axl_true if both string are equal, axl_false if not. If
 2054:  * some value provided is NULL or the size to compare is not greater
 2055:  * than 0 the function will return axl_false directly.
 2056:  */
 2057: axl_bool        axl_stream_casecmp           (const char * chunk1, const char * chunk2, int size)
 2058: {
 2059: 	/* perform some environmental condition checking */
 2060: 	if (chunk1 == NULL)
 2061: 		return axl_false;
 2062: 	if (chunk2 == NULL)
 2063: 		return axl_false;
 2064: 	if (size < 0)
 2065: 		return axl_false;
 2066: 
 2067: 	/* returh if both strings are equal. */
 2068: #if defined(AXL_OS_WIN32)
 2069: 	return _strnicmp (chunk1, chunk2, size) == 0;
 2070: #else
 2071: 	return strncasecmp (chunk1, chunk2, size) == 0;
 2072: #endif
 2073: }
 2074: 
 2075: /** 
 2076:  * @internal
 2077:  *
 2078:  * @brief Allows to check if the provided stream could support
 2079:  * checking a chunk of, at least, the provided inspected size.
 2080:  * 
 2081:  * @param stream The stream where the inspected operation is being
 2082:  * requested.
 2083:  *
 2084:  * @param inspected_size The size to inspected, that is being
 2085:  * requested to be checked on the given stream.
 2086:  * 
 2087:  * @return \ref axl_true if the provided chunk falls out side the
 2088:  * stream boundaries, or axl_false if requested inspected size could
 2089:  * be supported.
 2090:  */
 2091: axl_bool axl_stream_fall_outside (axlStream * stream, int inspected_size)
 2092: {
 2093: 	/* if the content is inside memory, check it */
 2094: 	if (fall_out_side_checking (stream, inspected_size)) {
 2095: 		return (! axl_stream_prebuffer (stream));
 2096: 	}
 2097: 	
 2098: 	/* otherwise, axl_false is returned */
 2099: 	return axl_false;
 2100: }
 2101: 
 2102: /** 
 2103:  * @internal
 2104:  *
 2105:  * @brief Allows to check if the given stream have as a next element
 2106:  * the provided chunk.
 2107:  * 
 2108:  * @param stream The stream where the operation will be performed.
 2109:  * @param chunk The chunk to check
 2110:  * 
 2111:  * @return Returns axl_true if the given stream contains the value requested
 2112:  * or axl_false if not.
 2113:  */
 2114: axl_bool         axl_stream_check           (axlStream * stream, char * chunk, int inspected_size)
 2115: {
 2116: 	int iterator;
 2117: 
 2118: 	/* call to the internal implementation of axl_memcmp */
 2119: 	_memcmp(iterator,chunk,(stream->stream + stream->stream_index), inspected_size);
 2120: 
 2121: }
 2122: 
 2123: /** 
 2124:  * @brief Allows to get current status of the stream. 
 2125:  *
 2126:  * If the is exhausted and have no more data to be read.
 2127:  * 
 2128:  * @param stream The stream that is being checked.
 2129:  * 
 2130:  * @return axl_true if the stream is exhausted or axl_false if not.
 2131:  */
 2132: axl_bool        axl_stream_remains         (axlStream * stream)
 2133: {
 2134: 	axl_return_val_if_fail (stream, axl_false);
 2135: 
 2136: 	
 2137: 	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking for stream status with stream index=%d and stream size=%d",
 2138: 		   stream->stream_index, stream->stream_size);
 2139: 		
 2140: 	/* check if the stream is exhausted */
 2141: 	if (stream->stream_index >= (stream->stream_size)) {
 2142: 
 2143: 		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "prebufferring from remains");
 2144: 
 2145: 		/* in the case the stream is exhausted, try to read
 2146: 		 * more content from the streaming */
 2147: 		return axl_stream_prebuffer (stream);
 2148: 	}
 2149: 	return axl_true;
 2150: }
 2151: 
 2152: 
 2153: /* @} */
 2154: 
 2155: /**
 2156:  * \defgroup axl_string_module Axl String: String functions provided by the Axl Stream module.
 2157:  */
 2158: 
 2159: /** 
 2160:  * \addtogroup axl_string_module
 2161:  * @{
 2162:  */
 2163: 
 2164: /** 
 2165:  *
 2166:  * @brief Allows to trim the provided chunk, removing all white spaces
 2167:  * (returns, white spaces, carry return and tabulars) that comes as
 2168:  * preffix and suffix for the string provided, referenced by chunk.
 2169:  *
 2170:  * The function retuns the reference to the new chunk already
 2171:  * translated. The function doesn't perform any memory allocation. It
 2172:  * uses the memory already used to hold the chunk provided, returning
 2173:  * a pointer for the first item that is not a white space and
 2174:  * nullifing the first item that is a white space behind the chunk.
 2175:  *
 2176:  * This function is particular useful while getting the content 
 2177:  * 
 2178:  * @param chunk The chunk to trim.
 2179:  *
 2180:  */
 2181: void      axl_stream_trim            (char * chunk)
 2182: {
 2183: 	/* call to trim */
 2184: 	axl_stream_trim_with_size (chunk, NULL);
 2185: 
 2186: 	return;
 2187: }
 2188: 
 2189: /** 
 2190:  * @brief The function works like \ref axl_stream_trim, but providing
 2191:  * the count of bytes trimmed from the string.
 2192:  *
 2193:  * @param chunk The chunk to trim.
 2194:  *
 2195:  * @param trimmed An optional reference that returns the count of bytes
 2196:  * trimmed by the operation.
 2197:  */
 2198: void        axl_stream_trim_with_size  (char * chunk, int * trimmed)
 2199: {
 2200: 	int    iterator;
 2201: 	int    iterator2;
 2202: 	int    end;
 2203: 	int    total;
 2204: 
 2205: 	/* perform some environment check */
 2206: 	axl_return_if_fail (chunk);
 2207: 
 2208: 	/* check empty string received */
 2209: 	if (strlen (chunk) == 0) {
 2210: 		if (trimmed)
 2211: 			*trimmed = 0;
 2212: 		return;
 2213: 	}
 2214: 
 2215: 	/* check the amount of white spaces to remove from the
 2216: 	 * begin */
 2217: 	iterator = 0;
 2218: 	while (chunk[iterator] != 0) {
 2219: 		
 2220: 		/* check that the iterator is not pointing to a white
 2221: 		 * space */
 2222: 		if (! axl_stream_is_white_space (chunk + iterator))
 2223: 			break;
 2224: 		
 2225: 		/* update the iterator */
 2226: 		iterator++;
 2227: 	}
 2228: 
 2229: 	/* check for the really basic case where an empty string is found */
 2230: 	if (iterator == strlen (chunk)) {
 2231: 		/* an empty string, trim it all */
 2232: 		chunk [0] = 0;
 2233: 		if (trimmed)
 2234: 			*trimmed = iterator;
 2235: 		return;
 2236: 	} /* end if */
 2237: 
 2238: 	/* now get the position for the last valid character in the
 2239: 	 * chunk */
 2240: 	total   = strlen (chunk) -1;
 2241: 	end     = total;
 2242: 	while (chunk[end] != 0) {
 2243: 		
 2244: 		/* stop if a white space is found */
 2245: 		if (! axl_stream_is_white_space (chunk + end)) {
 2246: 			break;
 2247: 		}
 2248: 
 2249: 		/* update the iterator to eat the next white space */
 2250: 		end--;
 2251: 	}
 2252: 
 2253: 	/* the number of items trimmed */
 2254: 	total -= end;
 2255: 	total += iterator;
 2256: 	
 2257: 	/* copy the exact amount of non white spaces items */
 2258: 	iterator2 = 0;
 2259: 	while (iterator2 < (end - iterator + 1)) {
 2260: 		/* copy the content */
 2261: 		chunk [iterator2] = chunk [iterator + iterator2];
 2262: 
 2263: 		/* update the iterator */
 2264: 		iterator2++;
 2265: 	}
 2266: 	chunk [ end - iterator + 1] = 0;
 2267: 
 2268: 	if (trimmed != NULL)
 2269: 		*trimmed = total;
 2270: 
 2271: 	/* return the result reference */
 2272: 	return;	
 2273: }
 2274: 
 2275: /** 
 2276:  * @brief Allows to remote occurences of value from the provided
 2277:  * string (chunk).
 2278:  *
 2279:  * The function do not allocate new memory for the result. All
 2280:  * operations are applied to the string received (chunk).
 2281:  *
 2282:  * The idea behind the functions is to allow removing values from the
 2283:  * string, joining remaining content. For example, removing "-" from
 2284:  * the string "iso-8859-15" yields "iso885915".
 2285:  * 
 2286:  * @param chunk The string that holds values to be removed.  
 2287:  *
 2288:  * @param value The value to be removed.
 2289:  *
 2290:  * @param first If only the first ocurrence of value must be removed,
 2291:  * otherwise all ocurrences will be removed from the string.
 2292:  */
 2293: void        axl_stream_remove            (char * chunk, const char * value, axl_bool first)
 2294: {
 2295: 	int iterator;
 2296: 	int iterator2;
 2297: 	int length;
 2298: 	int v_length;
 2299: 
 2300: 	axl_return_if_fail (chunk);
 2301: 	axl_return_if_fail (value);
 2302: 
 2303: 	/* get lengths */
 2304: 	length   = strlen (chunk);
 2305: 	v_length = strlen (value);
 2306: 
 2307: 	/* check for basic cases */
 2308: 	if (length == v_length) {
 2309: 		/* check if both strings are equal, then nullify */
 2310: 		if (axl_cmp (chunk, value))
 2311: 			chunk [0] = 0;
 2312: 		return;
 2313: 	} else if (length < v_length) {
 2314: 		/* nothing to remove because parent string is too
 2315: 		 * small to store the content */
 2316: 		return;
 2317: 	} /* end if */
 2318: 		
 2319: 
 2320: 	/* locate the string */
 2321: 	iterator = 0;
 2322: 	while (iterator < length) {
 2323: 		/* check if the string value was found */
 2324: 		if (axl_memcmp (chunk + iterator, value, v_length)) {
 2325: 			/* string found, move content remaining (if is found) */
 2326: 			if ((length - iterator - v_length) > 0) {
 2327: 				iterator2 = 0;
 2328: 				while (iterator2 < (length - iterator - v_length)) {
 2329: 					chunk [iterator + iterator2] = chunk [iterator + iterator2 + v_length];
 2330: 					iterator2++;
 2331: 				} /* end while */
 2332: 			} /* end if */
 2333: 			
 2334: 			/* update length to the new value */
 2335: 			length -= v_length;
 2336: 
 2337: 			/* check to terminate for first ocurrence */
 2338: 			if (first) {
 2339: 				chunk [length] = 0;
 2340: 				return;
 2341: 			}
 2342: 			continue;
 2343: 		} /* end if */
 2344: 
 2345: 		/* next position */
 2346: 		iterator++;
 2347: 	} /* end while */
 2348: 
 2349: 	/* nullify and terminate */
 2350: 	chunk [length] = 0;
 2351: 	return;
 2352: }
 2353: 
 2354: /** 
 2355:  * @brief Allows to copy the given chunk, supposing that is a properly
 2356:  * format C string that ends with a '\\0' value.
 2357:  *
 2358:  * This function allows to perform a copy for the given string. If a
 2359:  * copy limited by a size is required, use \ref axl_stream_strdup_n.
 2360:  * 
 2361:  * @param chunk The chunk to copy
 2362:  * 
 2363:  * @return A newly allocated string or NULL if fails.
 2364:  */
 2365: char      * axl_stream_strdup          (const char * chunk)
 2366: {
 2367: 	char * result;
 2368: 	int    length;
 2369: 
 2370: 	/* return NULL reference if a NULL reference is received */
 2371: 	if (chunk == NULL)
 2372: 		return NULL;
 2373: 
 2374: 	length = strlen (chunk);
 2375: 	result = axl_new (char, length + 1);
 2376: 	
 2377: 	memcpy (result, chunk, length);
 2378: 
 2379: 	return result;
 2380: }
 2381: 
 2382: /** 
 2383:  * @brief Allows to perform a copy for the <b>n</b> first bytes from
 2384:  * the <b>chunk</b> received.
 2385:  * 
 2386:  * @param chunk The chunk to copy
 2387:  *
 2388:  * @param n How many bytes to copy from the given chunk.
 2389:  * 
 2390:  * @return A newly allocated chunk, copied from the given chunk with a
 2391:  * size of <b>n</b> bytes. The function will check that the
 2392:  * <b>chunk</b> and the <b>n</b> values are not null and non-zero.
 2393:  */
 2394: char      * axl_stream_strdup_n (const char * chunk, int n)
 2395: {
 2396: 	char * result;
 2397: 
 2398: 	axl_return_val_if_fail (chunk, NULL);
 2399: 	axl_return_val_if_fail (n, NULL);
 2400: 	
 2401: 	result = axl_new (char, n + 1);
 2402: 	memcpy (result, chunk, n);
 2403: 	
 2404: 	return result;
 2405: }
 2406: 
 2407: /** 
 2408:  * @internal Allows to calculate the amount of memory required to
 2409:  * store the string that will representing the construction provided
 2410:  * by the printf-like format received and its arguments.
 2411:  * 
 2412:  * @param format The printf-like format to be printed.
 2413:  *
 2414:  * @param args The set of arguments that the printf applies to.
 2415:  *
 2416:  * <i><b>NOTE:</b> not all printf specification is supported. Generally, the
 2417:  * following is supported: %s, %d, %f, %g, %ld, %lg and all
 2418:  * combinations that provides precision, number of items inside the
 2419:  * integer part, etc: %6.2f, %+2d, etc. An especial case not supported
 2420:  * is %lld, %llu and %llg.</i>
 2421:  *
 2422:  * @return Return the number of bytes that must be allocated to hold
 2423:  * the string (including the string terminator \0). If the format is
 2424:  * not correct or it is not properly formated according to the value
 2425:  * found at the argument set, the function will return -1.
 2426:  */
 2427: int axl_stream_vprintf_len (const char * format, va_list args)
 2428: {
 2429: 	/** IMPLEMENTATION NOTE: in the case this code is update,
 2430: 	 * update exarg_vprintf_len **/
 2431: 
 2432: #if defined (AXL_OS_WIN32) && ! defined (__GNUC__)
 2433: #   if HAVE_VSCPRINTF
 2434: 	if (format == NULL)
 2435: 		return 0;
 2436: 	return _vscprintf (format, args) + 1;
 2437: #   else
 2438: 	char buffer[8192];
 2439: 	if (format == NULL)
 2440: 		return 0;
 2441: 	return _vsnprintf (buffer, 8191, format, args) + 1;
 2442: #   endif
 2443: #else
 2444: 	/* gnu gcc case */
 2445: 	if (format == NULL)
 2446: 		return 0;
 2447: 	return vsnprintf (NULL, 0, format, args) + 1;
 2448: #endif
 2449: }
 2450: 
 2451: /** 
 2452:  * @brief Allows to perform a printf operation on the provided buffer
 2453:  * (which must be allocated by the caller, and its size signaled by
 2454:  * buffer_size).
 2455:  *
 2456:  *
 2457:  * @param buffer The already allocated buffer to hold the result.
 2458:  *
 2459:  * @param buffer_size The size of the buffer provided. 
 2460:  *
 2461:  * @param real_size Optional reference where the real space required
 2462:  * to hold the content will be placed. In cases where the content is
 2463:  * enough small to hold in the buffer, this value will contain the
 2464:  * same value as returned by the function. In the case the buffer
 2465:  * provide can't hold all the content, the function will return at
 2466:  * maximum (buffer_size - 1) bytes written, that is, all content that
 2467:  * was possible to be included plus a trailing \\0 to terminate the
 2468:  * string, and, if defined <i>real_size</i> variable, it will contain
 2469:  * the space that will be required.
 2470:  *
 2471:  * @param format The printf like format to use to create the content
 2472:  * to be placed at the buffer provided.
 2473:  *
 2474:  * @return The amount of bytes written. The function will return at
 2475:  * maximum buffer_size - 1 bytes written. Use <i>real_size</i>
 2476:  * variable to check if the function was able to write all content (if
 2477:  * real_size == value returned). 
 2478:  */
 2479: int axl_stream_printf_buffer (char * buffer, 
 2480: 			      int    buffer_size, 
 2481: 			      int  * real_size,
 2482: 			      const char * format, ...)
 2483: {
 2484: 	va_list args;
 2485: 	int     result;
 2486: #if defined(AXL_OS_WIN32)
 2487: 	int     check_size;
 2488: #endif 
 2489: 
 2490: 	/* check foramt and optn */
 2491: 	if (format == NULL) {
 2492: 		/* clean real size if it was defined */
 2493: 		if (real_size)
 2494: 			(*real_size) = 0;
 2495: 		return 0;
 2496: 	}
 2497: 	/* open stdargs */
 2498: 	va_start (args, format);
 2499: 
 2500: # if defined (AXL_OS_WIN32)
 2501: 	/* because undre windows all its string family of functions
 2502: 	 * return -1 in the case not enough espace is available, we
 2503: 	 * have to check first before calling to _vsnprintf */
 2504: 	check_size = axl_stream_vprintf_len (format, args);
 2505: 
 2506: 	/* call to close to avoid problems with amd64 platforms */
 2507: 	va_end (args);
 2508: 	va_start (args, format);
 2509: 
 2510: 	/* windows case */
 2511: 	result = _vsnprintf (buffer, buffer_size, format, args);
 2512: 	if (result == -1) {
 2513: 		/* nullify last character and update "result" to point
 2514: 		 * to the amount of data written (buffer - 1). */
 2515: 		result = (buffer_size - 1);
 2516: 		buffer[result] = 0; 
 2517: 	} /* end if */
 2518: #else
 2519: 	/* gnu gcc case */
 2520: 	result = vsnprintf (buffer, buffer_size, format, args);
 2521: #endif
 2522: 	/* close stdarg */
 2523: 	va_end (args);
 2524: 
 2525: 	/* report real size required */
 2526: 	if (real_size) {
 2527: #if defined(AXL_OS_WIN32)
 2528: 		(*real_size) = check_size - 1;
 2529: #else
 2530: 		(*real_size) = result;
 2531: #endif
 2532: 	} /* end if */
 2533: 
 2534: 	/* limit result */
 2535: 	if (result > (buffer_size - 1))
 2536: 		result = (buffer_size - 1);
 2537: 
 2538: 	return result;
 2539: }
 2540: 
 2541: /** 
 2542:  * @internal Function that allows to get how many bytes will be
 2543:  * required to hold the format and the arguments provided.
 2544:  * 
 2545:  * @param format The printf-like format followed by the rest of
 2546:  * arguments.
 2547:  * 
 2548:  * @return Return the number of bytes that must be allocated to hold
 2549:  * the string (including the string terminator \0). If the format is
 2550:  * not correct or it is not properly formated according to the value
 2551:  * found at the argument set, the function will return -1.
 2552:  */
 2553: int axl_stream_printf_len (const char * format, ...)
 2554: {
 2555: 	int     result;
 2556: 	va_list args;
 2557: 
 2558: 	va_start (args, format);
 2559: 
 2560: 	/* get the result */
 2561: 	result = axl_stream_vprintf_len (format, args);
 2562: 	
 2563: 	va_end (args);
 2564: 
 2565: 	return result;
 2566: }
 2567: 
 2568: 
 2569: /** 
 2570:  * @brief Allows to produce an newly allocated string produced by the
 2571:  * chunk received plus arguments, using the printf-like format.
 2572:  *
 2573:  * @param chunk The chunk to copy.
 2574:  * 
 2575:  * @return A newly allocated chunk.
 2576:  */
 2577: char      * axl_stream_strdup_printf   (const char * chunk, ...)
 2578: {
 2579: 	char    * result   = NULL;
 2580: 	va_list   args;
 2581: 	
 2582: 	axl_return_val_if_fail (chunk, NULL);
 2583: 
 2584: 	/* open std args */
 2585: 	va_start (args, chunk);
 2586: 
 2587: 	/* get the string */
 2588: 	result = axl_stream_strdup_printfv (chunk, args);
 2589: 	
 2590: 	/* close std args */
 2591: 	va_end (args);
 2592: 	
 2593: 	return result;
 2594: }
 2595: 
 2596: /** 
 2597:  * @brief DEPRECATED: Allows to produce an string representing the
 2598:  * message hold by chunk with the parameters provided.
 2599:  * 
 2600:  * @param chunk The message chunk to print.
 2601:  * @param args The arguments for the chunk.
 2602:  * 
 2603:  * @return A newly allocated string.
 2604:  *
 2605:  * IMPLEMENTATION NOTE: This function may have a fundamental bug due
 2606:  * to the design of va_list arguments under amd64 platforms. In short,
 2607:  * a function receiving a va_list argument can't use it twice. In you
 2608:  * are running amd64, check your axl_config.h did find
 2609:  * AXL_HAVE_VASPRINTF.
 2610:  */
 2611: char  * axl_stream_strdup_printfv    (const char * chunk, va_list args)
 2612: {
 2613: 	/** IMPLEMENTATION NOTE: place update exarg_strdup_printfv
 2614: 	 * code in the case this code is updated **/
 2615: 
 2616: #ifndef AXL_HAVE_VASPRINTF
 2617: 	int       size;
 2618: #endif
 2619: 	char    * result   = NULL;
 2620: 
 2621: 	axl_return_val_if_fail (chunk, NULL);
 2622: 
 2623: #ifdef AXL_HAVE_VASPRINTF
 2624: 	/* do the operation using the GNU extension */
 2625: 	if (vasprintf (&result, chunk, args) == -1)
 2626: 		return NULL;
 2627: #else
 2628: 	/* get the amount of memory to be allocated */
 2629: 	size = axl_stream_vprintf_len (chunk, args);
 2630: 
 2631: 	/* check result */
 2632: 	if (size == -1) {
 2633: 		__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "unable to calculate the amount of memory for the strdup_printf operation");
 2634: 		return NULL;
 2635: 	} /* end if */
 2636: 
 2637: 	/* allocate memory */
 2638: 	result   = axl_new (char, size + 2);
 2639: 	
 2640: 	/* copy current size */
 2641: #    if defined(AXL_OS_WIN32) && ! defined (__GNUC__)
 2642: 	_vsnprintf_s (result, size + 1, size, chunk, args);
 2643: #    else
 2644: 	vsnprintf (result, size + 1, chunk, args);
 2645: #    endif
 2646: #endif
 2647: 	/* return the result */
 2648: 	return result;
 2649: }
 2650: 
 2651: /** 
 2652:  * @brief Allows to create a newly allocated chunk, providing its
 2653:  * values as a printf call function, but also returning the chunk
 2654:  * size.
 2655:  *
 2656:  * This function works like \ref axl_stream_strdup_printf, but
 2657:  * providing an integer reference where the result chunk length will
 2658:  * be returned. 
 2659:  * 
 2660:  * @param chunk The printf chunk format to allocate.
 2661:  *
 2662:  * @param chunk_size A reference to fill the chunk lenght.
 2663:  * 
 2664:  * @return A newly allocated chunk.
 2665:  */
 2666: char    * axl_stream_strdup_printf_len (const char * chunk, int * chunk_size, ...)
 2667: {
 2668: #ifndef AXL_HAVE_VASPRINTF
 2669: 	int       size;
 2670: #endif
 2671: 	int       new_size;
 2672: 	char    * result;
 2673: 	va_list   args;
 2674: 	
 2675: 	axl_return_val_if_fail (chunk, NULL);
 2676: 
 2677: 	/* open std args */
 2678: 	va_start (args, chunk_size);
 2679: 
 2680: #ifdef AXL_HAVE_VASPRINTF
 2681: 	/* do the operation using the GNU extension */
 2682: 	new_size = vasprintf (&result, chunk, args);
 2683: 
 2684: 	/* reopen to avoid amd64 bug */
 2685: 	va_end (args);
 2686: 	va_start (args, chunk_size);
 2687: #else
 2688: 	/* get the amount of memory to be allocated */
 2689: 	size = axl_stream_vprintf_len (chunk, args);
 2690: 
 2691: 	/* reopen to avoid amd64 bug */
 2692: 	va_end (args);
 2693: 	va_start (args, chunk_size);
 2694: 	
 2695: 	/* check result */
 2696: 	if (size == -1) {
 2697: 		__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "unable to calculate the amount of memory for the strdup_printf operation");
 2698: 		return NULL;
 2699: 	} /* end if */
 2700: 
 2701: 	/* allocate memory */
 2702: 	result   = axl_new (char, size + 2);
 2703: 
 2704: 	/* copy current size */
 2705: #if defined(AXL_OS_WIN32) && ! defined (__GNUC__)
 2706: 	new_size = _vsnprintf_s (result, size + 1, size, chunk, args);
 2707: #else
 2708: 	new_size = vsnprintf (result, size + 1, chunk, args);
 2709: #endif
 2710: #endif
 2711: 	
 2712: 	/* close std args */
 2713: 	va_end (args);
 2714: 
 2715: 	/* fill the chunk size result */
 2716: 	if (chunk_size != NULL)
 2717: 		*chunk_size = new_size;
 2718: 
 2719: 	return result;
 2720: }
 2721: 
 2722: /** 
 2723:  * @brief Allows to split the provided chunk, into several pieces that
 2724:  * are separated by the separator (or separators) provided.
 2725:  *
 2726:  * The function will try to split the chunk provide using the
 2727:  * separator provided, and optionally, all separators provided.
 2728:  *
 2729:  * Here is an example:
 2730:  * \code
 2731:  * char ** result;
 2732:  *
 2733:  * // split the provided value using the ':', ';' and ',' as separators.
 2734:  * result = axl_stream_split (value, 3, ":", ";", ",");
 2735:  * \endcode
 2736:  *
 2737:  * The value returned must be deallocated using \ref axl_stream_freev.
 2738:  *
 2739:  * @param chunk The chunk to split.
 2740:  *
 2741:  * @param separator_num The number os separators to be used while
 2742:  * spliting the chunk.
 2743:  * 
 2744:  * @return A newly allocated string, that must be deallocated by using
 2745:  * \ref axl_stream_freev. The function will return a NULL if the chunk
 2746:  * or the separators provided are NULL.
 2747:  *
 2748:  * NOTE: See also \ref axl_split.
 2749:  */
 2750: char     ** axl_stream_split           (const char * chunk, int separator_num, ...)
 2751: {
 2752: 	va_list      args;
 2753: 	char      ** separators;
 2754: 	char      ** result;
 2755: 	int          iterator;
 2756: 	int          index;
 2757: 	int          previous_index;
 2758: 	int          count      = 0;
 2759: 	int          length     = 0;
 2760: 
 2761: 	/* check received values */
 2762: 	axl_return_val_if_fail (chunk, NULL);
 2763: 	axl_return_val_if_fail (separator_num > 0, NULL);
 2764: 
 2765: 	separators = axl_new (char *, separator_num + 1);
 2766: 	iterator   = 0;
 2767: 	va_start (args, separator_num);
 2768: 
 2769: 	/* get all separators to be used */
 2770: 	while (iterator < separator_num) {
 2771: 		separators[iterator] = va_arg (args, char *);
 2772: 		iterator++;
 2773: 	} /* end if */
 2774: 	
 2775: 	va_end (args);
 2776: 
 2777: 	/* now, count the number of strings that we will get by
 2778: 	 * separating the string into several pieces */
 2779: 	index    = 0;
 2780: 	while (*(chunk + index) != 0) {
 2781: 
 2782: 		/* reset the iterator */
 2783: 		iterator = 0;
 2784: 		while (iterator < separator_num) { 
 2785: 
 2786: 			/* compare the current index with the current
 2787: 			 * separator */
 2788: 			length = strlen (separators[iterator]);
 2789: 			if (axl_memcmp (chunk + index, separators[iterator], length)) {
 2790: 
 2791: 				/* update items found */
 2792: 				count++;
 2793: 
 2794: 				/* update index to skip the item found */
 2795: 				index += length - 1; /* make the last index to be captured the the -1 */
 2796: 
 2797: 				/* break the loop */
 2798: 				break;
 2799: 			}
 2800: 			iterator++;
 2801: 		}
 2802: 
 2803: 		/* update the index to the next item */
 2804: 		index++;
 2805: 	} /* end if */
 2806: 	
 2807: 	/* create the result that will hold items separated */
 2808: 	result = axl_new (char *, count + 2);
 2809: 
 2810: 	/* now copy items found */
 2811: 	count          = 0;
 2812: 	index          = 0;
 2813: 
 2814: 	/* remember previous_index */
 2815: 	previous_index = index;
 2816: 	while (*(chunk + index) != 0) {
 2817: 
 2818: 		/* reset the iterator */
 2819: 		iterator = 0;
 2820: 		while (iterator < separator_num) { 
 2821: 
 2822: 			/* compare the current index with the current
 2823: 			 * separator */
 2824: 			length = strlen (separators[iterator]);
 2825: 			if (axl_memcmp (chunk + index, separators[iterator], length)) {
 2826: 
 2827: 				/* copy the chunk found */
 2828: 				result[count] = axl_new (char, index - previous_index + 1);
 2829: 				memcpy (result[count], chunk + previous_index, index - previous_index);
 2830: 
 2831: 				/* axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "item found (last) '%s' (index=%d != previous=%d", result[count],  index, previous_index); */
 2832: 
 2833: 				/* update items found */
 2834: 				count++;
 2835: 
 2836: 				/* update index to skip the item found */
 2837: 				if (*(chunk + index + length) == 0) {
 2838: 					/* in the case no more elements to read will be found */
 2839: 					/* put an empty space at the end */
 2840: 					result [count]    = axl_new (char, 1);
 2841: 					
 2842: 					axl_free (separators);
 2843: 					return result;
 2844: 				}
 2845: 
 2846: 				/* remember previous_index */
 2847: 				index += length; 
 2848: 				previous_index = index;
 2849: 				index--; /* make the last index to be captured the the -1 */
 2850: 				break;
 2851: 			}
 2852: 			iterator++;
 2853: 		}
 2854: 
 2855: 		/* update the index to the next item */
 2856: 		index++;
 2857: 	}
 2858: 
 2859: 	/* check for a last chunk */
 2860: 	if (index != previous_index) {
 2861: 		/* copy the chunk found */
 2862: 		result[count] = axl_new (char, index - previous_index + 1);
 2863: 		memcpy (result[count], chunk + previous_index, index - previous_index);
 2864: 
 2865: 		/* axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "item found (last) '%s' (index=%d != previous=%d", result[count], index, previous_index); */
 2866: 	}
 2867: 
 2868: 	
 2869: 	/* release memory */
 2870: 	axl_free (separators);
 2871: 	
 2872: 	return result;
 2873: }
 2874: 
 2875: /** 
 2876:  * @brief Allows to clean an split created by \ref axl_stream_split by
 2877:  * removing all items found to be empty strings.
 2878:  * 
 2879:  * @param split The split to be updated by removing all empty string
 2880:  * items.
 2881:  *
 2882:  */
 2883: void        axl_stream_clean_split     (char ** split)
 2884: {
 2885: 	int iterator;
 2886: 	int iterator2;
 2887: 	int iterator3;
 2888: 
 2889: 	/* check input */
 2890: 	axl_return_if_fail (split);
 2891: 
 2892: 	/* remove empty strings */
 2893: 	iterator = 0;
 2894: 	while (split[iterator]) {
 2895: 		if (strlen (split[iterator]) == 0) {
 2896: 
 2897: 			/* clear position joint */
 2898: 			axl_free (split[iterator]);
 2899: 			split[iterator] = NULL;
 2900: 
 2901: 			/* move strings */
 2902: 			iterator3 = 0;
 2903: 			iterator2 = iterator + 1;
 2904: 			while (split[iterator2 + iterator3]) {
 2905: 				/* move reference */
 2906: 				split[iterator + iterator3]  = split[iterator2 + iterator3];
 2907: 				
 2908: 				/* nullify */
 2909: 				split[iterator2 + iterator3] = NULL;
 2910: 				
 2911: 				/* next position */
 2912: 				iterator3++;
 2913: 			} /* end while */
 2914: 			continue;
 2915: 		} /* end if */
 2916: 
 2917: 		/* next iterator */
 2918: 		iterator++;
 2919: 	} /* end while */
 2920: 
 2921: 	return;
 2922: }
 2923: 
 2924: /** 
 2925:  * @brief Allows to implement the oposite operation of \ref
 2926:  * axl_stream_split, by joing all strings provided inside the array
 2927:  * (strings), using as separator the value provided.
 2928:  * 
 2929:  * @param strings The set of strings to be joined.
 2930:  *
 2931:  * @param separator The separator to be used to join all strings
 2932:  * provided.
 2933:  *
 2934:  * 
 2935:  * @return A newly allocated reference, that must be release using
 2936:  * \ref axl_free.
 2937:  *
 2938:  * NOTE: See also \ref axl_join.
 2939:  */
 2940: char      * axl_stream_join            (char      ** strings, 
 2941: 					const char * separator)
 2942: {
 2943: 	int         length;
 2944: 	int         sep_length;
 2945: 	int         iterator;
 2946: 	char      * result;
 2947: 	axl_bool    next_sep;
 2948: 
 2949: 	axl_return_val_if_fail (strings && strings[0], NULL);
 2950: 	axl_return_val_if_fail (separator, NULL);
 2951: 
 2952: 	/* get the amount of data to be allocated */
 2953: 	length   = 0;
 2954: 	iterator = 0;
 2955: 
 2956: 	/* for each value to be joined */
 2957: 	while (strings [iterator]) {
 2958: 		/* count the number of bytes for each string */
 2959: 		length += strlen (strings[iterator]);
 2960: 
 2961: 		/* next iterator */
 2962: 		iterator++;
 2963: 	} /* end while */
 2964: 
 2965: 	/* check for the basic case */
 2966: 	if (iterator == 1) {
 2967: 		/* only one piece is contained in the set of strings
 2968: 		 * provided, so nothing can be joined */
 2969: 		return axl_strdup (strings[0]);
 2970: 	}
 2971: 	
 2972: 	/* add to the length the number of separatos to be added
 2973: 	 * (wihtout 1) and add a traling byte to terminate the
 2974: 	 * string */
 2975: 	sep_length = strlen (separator);
 2976: 	length    += (sep_length * (iterator - 1)) + 1;
 2977: 	result     = axl_new (char, length);
 2978: 
 2979: 	iterator   = 0;
 2980: 	next_sep   = axl_false;
 2981: 	length     = 0;
 2982: 
 2983: 	while (strings [iterator]) {
 2984: 
 2985: 		/* copy the content */
 2986: 		if (next_sep) {
 2987: 			memcpy (result + length, separator, sep_length);
 2988: 
 2989: 			/* update the length */
 2990: 			length += sep_length;
 2991: 		} else {
 2992: 			memcpy (result + length, strings[iterator], strlen (strings[iterator]));
 2993: 
 2994: 			/* update the length */
 2995: 			length += strlen (strings[iterator]);
 2996: 		} /* end if */
 2997: 
 2998: 		/* check if next is separator */
 2999: 		next_sep = ! next_sep;
 3000: 
 3001: 		/* update the iterator only if next value to be
 3002: 		 * handled is a separator */
 3003: 		if (next_sep) 
 3004: 			iterator++;
 3005: 	} /* end while */
 3006: 
 3007: 	/* return string created */
 3008: 	return result;
 3009: }
 3010: 
 3011: /** 
 3012:  * @brief Allows to replace the provided string by the provided
 3013:  * replacement on the provided source string, doing the replacement in
 3014:  * an effective manner.
 3015:  *
 3016:  * @param source The source string where to look and replace. The
 3017:  * result will be reported as a new pointer on this parameter. The
 3018:  * function will dealloc previous string (passed in source).
 3019:  *
 3020:  * @param source_len The replace function can handle binary
 3021:  * strings. This parameter signals the function how many bytes are
 3022:  * found on the source pointer.
 3023:  *
 3024:  * @param string The string that will be looked for replacement. The
 3025:  * string can be binary data but its length must be configured.
 3026:  *
 3027:  * @param string_len String length or -1. The string parameter can be
 3028:  * binary data (may include \0) but length must be especified. If -1
 3029:  * is provided, the function will use strlen () to get current string
 3030:  * size.
 3031:  *
 3032:  * @param replacement The replace string. The replacement can be
 3033:  * binary data but its length must be configured.
 3034:  *
 3035:  * @param replacement_len Replacement string length or -1. The
 3036:  * replacement parameter can be binary data (may include \0) but
 3037:  * length must be especified. If -1 is provided, the function will use
 3038:  * strlen () to get current string size.
 3039:  *
 3040:  * @return The function returns the new size of the string. The
 3041:  * function returns the same source length when no replacement was
 3042:  * done. The function return source_len in the case some argument is NULL.
 3043:  */
 3044: int  axl_stream_replace         (char        ** source,      int source_len, 
 3045: 				 const char   * string,      int string_len, 
 3046: 				 const char   * replacement, int replacement_len)
 3047: {
 3048: 	int    iterator;
 3049: 	int    iterator2;
 3050: 	int    count;
 3051: 	char * result;
 3052: 	int    old_source_len;
 3053: 
 3054: 	/* check arguments */
 3055: 	axl_return_val_if_fail (source && string && replacement, source_len);
 3056: 
 3057: 	/* get sizes if not configured */
 3058: 	if (source_len == -1)
 3059: 		source_len = strlen (*source);
 3060: 	if (string_len == -1)
 3061: 		string_len = strlen (string);
 3062: 	if (replacement_len == -1)
 3063: 		replacement_len = strlen (replacement);
 3064: 
 3065: 	/* find how many strings must be replaced */
 3066: 	iterator = 0;
 3067: 	count    = 0;
 3068: 	while ((iterator + string_len - 1) < source_len) {
 3069: 		/* check if the string is found */
 3070: 		if (axl_memcmp ((*source) + iterator, string, string_len)) {
 3071: 			/* string found ! */
 3072: 			count++;
 3073: 			
 3074: 			/* skip these bytes */
 3075: 			iterator += string_len;
 3076: 			continue;
 3077: 		}
 3078: 
 3079: 		/* next position */
 3080: 		iterator++;
 3081: 	} /* end while */
 3082: 
 3083: 	/* check if we have found some to replace */
 3084: 	if (count == 0)
 3085: 		return source_len;
 3086: 
 3087: 	/* update source length */
 3088: 	old_source_len = source_len;
 3089: 	source_len     = source_len - (string_len * count) + (replacement_len * count);
 3090: 
 3091: 	/* alloc memory for the replacement */
 3092: 	result     = axl_new (char, source_len + 1);
 3093: 
 3094: 	/* do replacement */
 3095: 	iterator  = 0;
 3096: 	iterator2 = 0;
 3097: 	while (iterator < old_source_len) {
 3098: 		/* check if the string is found */
 3099: 		if (((iterator + string_len - 1) < old_source_len) && axl_memcmp ((*source) + iterator, string, string_len)) {
 3100: 			/* string found!, replace */
 3101: 			memcpy (result + iterator2, replacement, replacement_len);
 3102: 			
 3103: 			/* skip these bytes */
 3104: 			iterator  += string_len;
 3105: 			iterator2 += replacement_len;
 3106: 			continue;
 3107: 		}
 3108: 
 3109: 		/* copy byte by byte */
 3110: 		result[iterator2] = (*source)[iterator];
 3111: 
 3112: 		/* next position */
 3113: 		iterator++;
 3114: 		iterator2++;
 3115: 	} /* end while */
 3116: 
 3117: 	/* release and report new string */
 3118: 	axl_free (*source);
 3119: 	*source = result;
 3120: 
 3121: 	return source_len;
 3122: }
 3123: 
 3124: /** 
 3125:  * @brief Allows to concatenate the two given strings into a single
 3126:  * one.
 3127:  *
 3128:  * The function will concatenate both string into a newly allocated
 3129:  * string that is the result of taking chunk1 followed by chunk2. 
 3130:  * 
 3131:  * If the function receive a NULL value for one of the string
 3132:  * received, the result from this function will be the other
 3133:  * string. This is done to support a common basic case where the
 3134:  * string provided for one of the arguments is the one being used two
 3135:  * hold an iterative result. This variable usually is NULL.
 3136:  *
 3137:  * Once the string returned is no longer needed, \ref axl_free must be
 3138:  * used to deallocate the result. 
 3139:  *
 3140:  * The function will use the strlen function to calculate current
 3141:  * chunk sizes to provide the result.
 3142:  *
 3143:  * 
 3144:  * @param chunk1 The first chunk to be placed on the result.
 3145:  *
 3146:  * @param chunk2 The second chunk to be placed on the result.
 3147:  * 
 3148:  * @return A newly allocated string, containing both strings, or NULL
 3149:  * if fails. The only way for this function to fail is to provide two
 3150:  * NULL references as incoming strings.
 3151:  *
 3152:  * NOTE: See also \ref axl_concat.
 3153:  */
 3154: char      * axl_stream_concat          (const char * chunk1, const char * chunk2)
 3155: {
 3156: 	char * result;
 3157: 	int    len1;
 3158: 	int    len2;
 3159: 
 3160: 	axl_return_val_if_fail ((chunk2 != NULL) || (chunk1 != NULL), NULL);
 3161: 
 3162: 	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "concat called..");
 3163: 
 3164: 	if (chunk1 == NULL) {
 3165: 
 3166: 		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "concat called.., returning %s", chunk2);
 3167: 
 3168: 		/* return the result */
 3169: 		return axl_strdup (chunk2);
 3170: 	}
 3171: 
 3172: 	if (chunk2 == NULL) {
 3173: 
 3174: 		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "concat called.., returning %s", chunk1);
 3175: 
 3176: 		/* return the result */
 3177: 		return axl_strdup (chunk1);
 3178: 	}
 3179: 
 3180: 	/* return the concatenation */
 3181: 	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "concat called.., returning %s%s", chunk1, chunk2);
 3182: 
 3183: 	/* alloc enough memory to hold both strings */
 3184: 	len1 = strlen (chunk1);
 3185: 	len2 = strlen (chunk2);
 3186: 	result = axl_new (char, len1 + len2 + 1);
 3187: 
 3188: 	/* copy the content */
 3189: 	memcpy (result, chunk1, len1);
 3190: 	memcpy (result + len1, chunk2, len2);
 3191: 
 3192: 	/* return the string created */
 3193: 	return result;
 3194: 
 3195: 
 3196: }
 3197: 
 3198: 
 3199: /** 
 3200:  * @brief Returns current number of items inside the chunks reference
 3201:  * provided.
 3202:  * 
 3203:  * @param chunks The chunks reference, which contains a list of
 3204:  * chunks.
 3205:  * 
 3206:  * @return The number of chunks that the reference has or -1 if it
 3207:  * fails.
 3208:  */
 3209: int         axl_stream_strv_num        (char ** chunks)
 3210: {
 3211: 	int iterator = 0;
 3212: 
 3213: 	axl_return_val_if_fail (chunks, -1);
 3214: 	/* release memory used by all elements inside the chunk */
 3215: 	while (chunks[iterator] != 0) {
 3216: 		iterator++;
 3217: 	}
 3218: 
 3219: 	/* return current number of chunks */
 3220: 	return iterator;
 3221: 	
 3222: }
 3223: 
 3224: /** 
 3225:  * @brief Allows to release memory used by elements returned by \ref
 3226:  * axl_stream_split and other function that return a pointer to a char **.
 3227:  * 
 3228:  * @param chunks The chunk to release.
 3229:  */
 3230: void        axl_stream_freev           (char ** chunks)
 3231: {
 3232: 	int iterator = 0;
 3233: 
 3234: 	axl_return_if_fail (chunks);
 3235: 	 
 3236: 	/* release memory used by all elements inside the chunk */
 3237: 	while (chunks[iterator] != 0) {
 3238: 		axl_free (chunks[iterator]);
 3239: 		iterator++;
 3240: 	}
 3241: 	
 3242: 	/* now release the chunk inside */
 3243: 	axl_free (chunks);
 3244: 	
 3245: 	/* nothing more to do */
 3246: 	return;
 3247: }
 3248: 
 3249: /** 
 3250:  * @internal 
 3251:  *
 3252:  * @brief Internal function to support \ref axl_stream_to_upper and
 3253:  * \ref axl_stream_to_lower
 3254:  * 
 3255:  * @param chunk The chunk to modify
 3256:  * @param desp Bits to increase.
 3257:  */
 3258: void __axl_stream_common_to (char * chunk, axl_bool to_upper)
 3259: {
 3260: 	int iterator = 0;
 3261: 
 3262: 	axl_return_if_fail (chunk);
 3263: 
 3264: 	while (chunk[iterator] != 0) {
 3265: 		/* change first ascii level */
 3266: 		if (to_upper)
 3267: 			chunk[iterator] = toupper (chunk[iterator]);
 3268: 		else
 3269: 			chunk[iterator] = tolower (chunk[iterator]);
 3270: 		
 3271: 		/* update iterators */
 3272: 		iterator++;
 3273: 	} 	
 3274: 
 3275: 	return;
 3276: }
 3277: 
 3278: /** 
 3279:  * @brief Makes the provided string to be converted to upper case
 3280:  * letters.
 3281:  *
 3282:  * The function will modify the address provided. If you want to get a
 3283:  * upper case copy for a particular string, copy it first, by using
 3284:  * \ref axl_strdup, and then use this function.
 3285:  *
 3286:  * The function, for convenience, will also return the string
 3287:  * reference received, already modified. This could be used while
 3288:  * using this function to convert parameters that are expected by
 3289:  * other functions.
 3290:  * 
 3291:  * @param chunk The chunk to upper case.
 3292:  */
 3293: char *    axl_stream_to_upper        (char  * chunk)
 3294: {
 3295: 	__axl_stream_common_to (chunk, axl_true);
 3296: 	return chunk;
 3297: }
 3298: 
 3299: /** 
 3300:  * @brief Allows to convert the provided string into lower cases
 3301:  * letter.
 3302:  *
 3303:  * The function, for convenience, will also return the string
 3304:  * reference received, already modified. This could be used while
 3305:  * using this function to convert parameters that are expected by
 3306:  * other functions.
 3307:  * 
 3308:  * @param chunk The chunk to lower case.
 3309:  */
 3310: char *     axl_stream_to_lower        (char  * chunk)
 3311: {
 3312: 	__axl_stream_common_to (chunk, axl_false);
 3313: 	return chunk;
 3314: }
 3315: 
 3316: /** 
 3317:  * @brief Allows to perform a to upper operation, like \ref
 3318:  * axl_stream_to_upper, but returning an new allocated reference.
 3319:  * 
 3320:  * @param chunk The string reference to upper.
 3321:  * 
 3322:  * @return A new reference allocated containing the result or NULL if
 3323:  * it fails.
 3324:  */
 3325: char      * axl_stream_to_upper_copy   (const char  * chunk)
 3326: {
 3327: 	char * result;
 3328: 
 3329: 	/* perform some environmental checks */
 3330: 	axl_return_val_if_fail (chunk, NULL);
 3331: 
 3332: 	/* make a copy */
 3333: 	result = axl_strdup (chunk);
 3334: 
 3335: 	/* upper it */
 3336: 	__axl_stream_common_to (result, axl_true);
 3337: 
 3338: 	/* return the result */
 3339: 	return result;
 3340: }
 3341: 
 3342: 
 3343: /** 
 3344:  * @brief Allows to perform a to lower operation, like \ref
 3345:  * axl_stream_to_upper, but returning an new allocated reference.
 3346:  * 
 3347:  * @param chunk The string reference to lower.
 3348:  * 
 3349:  * @return A new reference allocated containing the result or NULL if
 3350:  * it fails.
 3351:  */
 3352: char      * axl_stream_to_lower_copy   (const char  * chunk)
 3353: {
 3354: 	char * result;
 3355: 
 3356: 	/* perform some environmental checks */
 3357: 	axl_return_val_if_fail (chunk, NULL);
 3358: 
 3359: 	/* make a copy */
 3360: 	result = axl_strdup (chunk);
 3361: 
 3362: 	/* lower it */
 3363: 	__axl_stream_common_to (result, axl_false);
 3364: 
 3365: 	/* return the result */
 3366: 	return result;
 3367: }
 3368: 
 3369: /** 
 3370:  * @brief Allows to compare two strings provided, s1 and s1 to be
 3371:  * equal.
 3372:  *
 3373:  * In the case both are equal, \ref axl_true is returned. Otherwise \ref
 3374:  * axl_false. The function compares that both are equal not only by making
 3375:  * the first to be contained inside the second string. The check also
 3376:  * ensures that "test" isn't equal to "test1".
 3377:  *
 3378:  * @param string First string to check.
 3379:  *
 3380:  * @param string2 Second string to check.
 3381:  * 
 3382:  * @return \ref axl_true if both string are equal, otherwise \ref axl_false is
 3383:  * returned.
 3384:  */
 3385: axl_bool axl_cmp (const char * string, const char * string2)
 3386: {
 3387: 	int iterator = 0;
 3388: 
 3389: 	if (string == NULL)
 3390: 		return axl_false;
 3391: 	if (string2 == NULL)
 3392: 		return axl_false;
 3393: 	
 3394: 	/* for each item inside the iterator */
 3395: 	while (string [iterator] != 0 && string2 [iterator] != 0) {
 3396: 		
 3397: 		/* check the content */
 3398: 		if (string [iterator] != string2 [iterator])
 3399: 			return axl_false;
 3400: 
 3401: 		/* update the iterator */
 3402: 		iterator++;
 3403: 		
 3404: 	} /* end while */
 3405: 	
 3406: 	/* check that both string ends at the same point */
 3407: 	if (string [iterator] != 0 ||
 3408: 	    string2 [iterator] != 0)
 3409: 		return axl_false;
 3410: 	
 3411: 	return axl_true;
 3412: }
 3413: 
 3414: axl_bool        axl_casecmp (const char * string, const char * string2)
 3415: {
 3416: 	int length;
 3417: 
 3418: 	if (string == NULL)
 3419: 		return axl_false;
 3420: 	if (string2 == NULL)
 3421: 		return axl_false;
 3422: 
 3423: 	/* get length associated to first string */
 3424: 	length = strlen (string);
 3425: 	if (length != strlen (string2))
 3426: 		return axl_false;
 3427: 	
 3428: 	/* now check both lengths */
 3429: 	return axl_stream_casecmp (string, string2, length);
 3430: 	
 3431: }
 3432: 
 3433: 
 3434: /** 
 3435:  * @brief Allows to check if both strings provided are equal on its
 3436:  * initial size bytes.
 3437:  *
 3438:  * This function is more efficient than common memcmp because it
 3439:  * doesn't perform the additional work to figure out which are the
 3440:  * bytes that differ both strings.
 3441:  * 
 3442:  * @param string The string to check.
 3443:  *
 3444:  * @param string2 The second string to check.
 3445:  *
 3446:  * @param size The size to check for both strings to be equal.
 3447:  * 
 3448:  * @return \ref axl_true if the both strings are equal for its initial
 3449:  * size bytes or \ref axl_false if not.
 3450:  */
 3451: axl_bool axl_memcmp (const char * string, const char * string2, int size)
 3452: {
 3453: 	int iterator = 0;
 3454: 
 3455: 	_memcmp(iterator,string,string2,size);
 3456: }
 3457: 
 3458: /** 
 3459:  *
 3460:  * @brief Perform a memory copy from the string provided.
 3461:  * 
 3462:  * @param string The string to copy.
 3463:  * 
 3464:  * @return A newly allocated value or NULL if it fails. The value
 3465:  * returned, must be deallocated using \ref axl_free.
 3466:  */
 3467: char * axl_strdup (const char * string)
 3468: {
 3469: 	return (string != NULL) ? (char *) axl_stream_strdup ((char *) string) : NULL;
 3470: }
 3471: 
 3472: /** 
 3473:  * @internal Function that handles decoding operations when decode
 3474:  * functions are defined.
 3475:  * 
 3476:  * @param stream The stream where the decode operation will be
 3477:  * performed. 
 3478:  *
 3479:  * @param error Optional \ref axlError reference where errors will be
 3480:  * reported.
 3481:  * 
 3482:  * @return axl_true if the operation was completed.
 3483:  */
 3484: axl_bool axl_stream_decode (axlStream  * stream, 
 3485: 			char       * output, 
 3486: 			int          output_max_size, 
 3487: 			int        * output_decoded, 
 3488: 			int        * op_result, 
 3489: 			axlError  ** error)
 3490: {
 3491: 
 3492: 	int result;
 3493: 	int size;
 3494: 
 3495: 	/* clear op_result if defined */
 3496: 	if (op_result)
 3497: 		*op_result = 0;
 3498: 
 3499: 	/* decode content from the stream directly */
 3500: 	result = stream->decode_f (
 3501: 		/* source */
 3502: 		stream->decode_temp,
 3503: 		/* source size */
 3504: 		stream->decode_temp_last,
 3505: 		/* source encoding: encode used by the source
 3506: 		 * content provided */
 3507: 		stream->source_encoding,
 3508: 		/* output of content decoded and its size */
 3509: 		output,
 3510: 		/* output max size */
 3511: 		output_max_size,
 3512: 		/* output decoded */
 3513: 		output_decoded,
 3514: 		/* source remaining */
 3515: 		&(stream->decode_temp_remain),
 3516: 		/* user defined data */
 3517: 		stream->decode_f_data);
 3518: 
 3519: 	/* set op result if defined */
 3520: 	if (op_result)
 3521: 		*op_result = result;
 3522: 	
 3523: 	/* check result */
 3524: 	if (result == 0) {
 3525: 		__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "after decode operation result=%d, output_decoded (new buffer size)=%d (from %d original bytes)",
 3526: 			   result, output_decoded ? *output_decoded : 0, stream->decode_temp_last);
 3527: 		axl_error_new (-1, "found internal failure at decode operation, unable to complete entity parsing",
 3528: 			       stream, error);
 3529: 		return axl_false;
 3530: 	} /* end if */
 3531: 	
 3532: 	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "after decode operation result=%d, output_decoded (new buffer size)=%d",
 3533: 		   result, output_decoded ? *output_decoded : 0);
 3534: 	
 3535: 	/* update axl stream internal state */
 3536: 	if (result == 1) {
 3537: 		/* then the conversión was complete a no data
 3538: 		 * is pending the the temporal decode
 3539: 		 * buffer */
 3540: 		stream->decode_temp_index = 0;
 3541: 		stream->decode_temp_last  = 0;
 3542: 	} else if (result == 2) {
 3543: 		/* not enough space was found at the destination
 3544: 		 * buffer, pack content at the decode buffer at the
 3545: 		 * next operation */
 3546: 		size = stream->decode_temp_last - stream->decode_temp_remain;
 3547: 		if (size <= 0) {
 3548: 			__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, 
 3549: 				   "found decode function return 2 (signaling pending data to be decoded) but last - remain yields to %d",
 3550: 				   size);
 3551: 			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);
 3552: 			return axl_false;
 3553: 		} /* end if */
 3554: 		
 3555: 		/* moving data */
 3556: 		while (stream->decode_temp_index < size) { 
 3557: 			stream->decode_temp[stream->decode_temp_index] = stream->decode_temp[stream->decode_temp_remain +  stream->decode_temp_index];
 3558: 			stream->decode_temp_index++;
 3559: 		} /* end while */
 3560: 		
 3561: 		/* now reset */
 3562: 		stream->decode_temp_index = 0;
 3563: 		stream->decode_temp_last  = size;
 3564: 		
 3565: 		/* reset to 1 since we have moved content to
 3566: 		 * the begin of the buffer */
 3567: 		result = 1;
 3568: 	} /* end if */
 3569: 
 3570: 	return (result == 1);
 3571: }
 3572: 
 3573: /** 
 3574:  * @internal Function used by the axl stream module to call to check
 3575:  * function defined.
 3576:  * 
 3577:  * @return axl_true if the check was fine, otherwise axl_false is returned.
 3578:  */
 3579: axl_bool axl_stream_content_check (axlStream * stream, const char * content, int content_length, axlError ** error)
 3580: {
 3581: 	if (stream == NULL || content == NULL) {
 3582: 		__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "content check function failed because null reference was received.");
 3583: 		axl_error_new (-1, "content check function failed because null reference was received.", stream, error);
 3584: 		return axl_false;
 3585: 	} /* end if */
 3586: 	
 3587: 	/* return appropiate value */
 3588: 	if (stream->check_f (content, content_length, stream->source_encoding, stream->check_f_data, error) == 1) 
 3589: 		return axl_true;
 3590: 
 3591: 	__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "content check function have failed");
 3592: 
 3593: 	/* check if error reference was defined */
 3594: 	return axl_false;
 3595: }
 3596: 
 3597: 
 3598: /** 
 3599:  * @internal Allows to configure a decode functions to be used to
 3600:  * translate content read into utf-8. 
 3601:  * 
 3602:  * @param stream The stream to be configured.
 3603:  *
 3604:  * @param source_encoding Original source encode.
 3605:  *
 3606:  * @param decode_f The function to be called to translate/check
 3607:  * content read into utf-8. If a null value is provided the decode
 3608:  * function is removed.
 3609:  *
 3610:  * @param error The reference where errors will be reported.
 3611:  *
 3612:  * @return axl_true if the function setup decode handler properly
 3613:  * otherwise axl_false is returned.
 3614:  */
 3615: axl_bool        axl_stream_setup_decode        (axlStream         * stream,
 3616: 						const char        * source_encoding,
 3617: 						axlStreamDecode     decode_f,
 3618: 						axlPointer          user_data,
 3619: 						axlError         ** error)
 3620: {
 3621: 	axl_return_val_if_fail (stream, axl_false);
 3622: 
 3623: 	/* do not check if the decode_f function is NULL (it's a valid
 3624: 	 * case) */
 3625: 	stream->decode_f      = decode_f;
 3626: 	stream->decode_f_data = user_data;
 3627: 
 3628: 	/* store source encoding */
 3629: 	if (source_encoding != NULL)
 3630: 		stream->source_encoding = axl_strdup (source_encoding);
 3631: 
 3632: 	/* call to check and decode if required bufferede content */
 3633: 	if (stream->decode_f) {
 3634: 		/* init decode buffer */
 3635: 		stream->decode_temp_size = (stream->buffer_size * 2) + 1;
 3636: 		stream->decode_temp      = axl_new (char, stream->decode_temp_size);
 3637: 
 3638: 		/* move content into decode temporal buffer */
 3639: 		memcpy (stream->decode_temp, 
 3640: 			stream->stream + stream->stream_index, 
 3641: 			stream->stream_size - stream->stream_index);
 3642: 		stream->decode_temp_index = 0;
 3643: 		stream->decode_temp_last  = stream->stream_size - stream->stream_index;
 3644: 		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "procesing %d bytes from decode buffer (total size: %d, current index: 0)", 
 3645: 			   stream->decode_temp_last, stream->decode_temp_size);
 3646: 
 3647: 		/* call to decode content */
 3648: 		if (! axl_stream_decode (stream, 
 3649: 					 /* output */
 3650: 					 (stream->stream + stream->stream_index),
 3651: 					 /* output max size */
 3652: 					 stream->buffer_size - stream->stream_index,
 3653: 					 /* output decoded */
 3654: 					 &(stream->stream_size),
 3655: 					 /* do not define op result */
 3656: 					 NULL,
 3657: 					 error))
 3658: 			return axl_false;
 3659: 		
 3660: 		/* add to the stream size the current index */
 3661: 		stream->stream_size += stream->stream_index;
 3662: 		
 3663: 	} /* end if */
 3664: 
 3665: 	/* return result */
 3666: 	return axl_true;
 3667: }
 3668: 
 3669: /** 
 3670:  * @brief Function that allows to configure a handler that is executed
 3671:  * to check content read into the axl stream buffer. See \ref
 3672:  * axlStreamContentCheck for more information.
 3673:  * 
 3674:  * @param stream The stream that is going to be configured.
 3675:  *
 3676:  * @param source_encoding The source encoding detected.
 3677:  *
 3678:  * @param check The function that implements the check.
 3679:  *
 3680:  * @param user_data User defined data to be passed to the check
 3681:  * function.
 3682:  *
 3683:  * @param error Optional \ref axlError reference where errors will be
 3684:  * reported.
 3685:  * 
 3686:  * @return The function returns axl_true if the cheker was installed and
 3687:  * first execution was completed.
 3688:  */
 3689: axl_bool    axl_stream_setup_check         (axlStream                * stream,
 3690: 					    const char               * source_encoding,
 3691: 					    axlStreamContentCheck      check,
 3692: 					    axlPointer                 user_data,
 3693: 					    axlError                ** error)
 3694: {
 3695: 	axl_return_val_if_fail (stream, axl_false);
 3696: 
 3697: 	/* do not check if the decode_f function is NULL (it's a valid
 3698: 	 * case) */
 3699: 	stream->check_f      = check;
 3700: 	stream->check_f_data = user_data;
 3701: 
 3702: 	/* store source encoding */
 3703: 	if (source_encoding != NULL) 
 3704: 		stream->source_encoding = axl_strdup (source_encoding);
 3705: 
 3706: 	if (stream->check_f) {
 3707: 		/* call to check */
 3708: 		if (! axl_stream_content_check (stream, stream->stream + stream->stream_index, stream->stream_size - stream->stream_index, error)) {
 3709: 			__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "content check function have failed, looks like there is a problem with content");
 3710: 			return axl_false;
 3711: 		} /* end if */
 3712: 	} /* end if */
 3713: 
 3714: 	return axl_true;
 3715: }
 3716: 
 3717: /* @} */
 3718: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>