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