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