File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libxml2 / buf.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 19:53:31 2014 UTC (10 years ago) by misho
Branches: libxml2, MAIN
CVS tags: v2_9_1p0, v2_9_1, HEAD
libxml2 2.9.1

    1: /*
    2:  * buf.c: memory buffers for libxml2
    3:  *
    4:  * new buffer structures and entry points to simplify the maintainance
    5:  * of libxml2 and ensure we keep good control over memory allocations
    6:  * and stay 64 bits clean.
    7:  * The new entry point use the xmlBufPtr opaque structure and
    8:  * xmlBuf...() counterparts to the old xmlBuf...() functions
    9:  *
   10:  * See Copyright for the status of this software.
   11:  *
   12:  * daniel@veillard.com
   13:  */
   14: 
   15: #define IN_LIBXML
   16: #include "libxml.h"
   17: 
   18: #include <string.h> /* for memset() only ! */
   19: #include <limits.h>
   20: #ifdef HAVE_CTYPE_H
   21: #include <ctype.h>
   22: #endif
   23: #ifdef HAVE_STDLIB_H
   24: #include <stdlib.h>
   25: #endif
   26: 
   27: #include <libxml/tree.h>
   28: #include <libxml/globals.h>
   29: #include <libxml/tree.h>
   30: #include "buf.h"
   31: 
   32: #define WITH_BUFFER_COMPAT
   33: 
   34: /**
   35:  * xmlBuf:
   36:  *
   37:  * A buffer structure. The base of the structure is somehow compatible
   38:  * with struct _xmlBuffer to limit risks on application which accessed
   39:  * directly the input->buf->buffer structures.
   40:  */
   41: 
   42: struct _xmlBuf {
   43:     xmlChar *content;		/* The buffer content UTF8 */
   44:     unsigned int compat_use;    /* for binary compatibility */
   45:     unsigned int compat_size;   /* for binary compatibility */
   46:     xmlBufferAllocationScheme alloc; /* The realloc method */
   47:     xmlChar *contentIO;		/* in IO mode we may have a different base */
   48:     size_t use;		        /* The buffer size used */
   49:     size_t size;		/* The buffer size */
   50:     xmlBufferPtr buffer;        /* wrapper for an old buffer */
   51:     int error;                  /* an error code if a failure occured */
   52: };
   53: 
   54: #ifdef WITH_BUFFER_COMPAT
   55: /*
   56:  * Macro for compatibility with xmlBuffer to be used after an xmlBuf
   57:  * is updated. This makes sure the compat fields are updated too.
   58:  */
   59: #define UPDATE_COMPAT(buf)				    \
   60:      if (buf->size < INT_MAX) buf->compat_size = buf->size; \
   61:      else buf->compat_size = INT_MAX;			    \
   62:      if (buf->use < INT_MAX) buf->compat_use = buf->use; \
   63:      else buf->compat_use = INT_MAX;
   64: 
   65: /*
   66:  * Macro for compatibility with xmlBuffer to be used in all the xmlBuf
   67:  * entry points, it checks that the compat fields have not been modified
   68:  * by direct call to xmlBuffer function from code compiled before 2.9.0 .
   69:  */
   70: #define CHECK_COMPAT(buf)				    \
   71:      if (buf->size != (size_t) buf->compat_size)	    \
   72:          if (buf->compat_size < INT_MAX)		    \
   73: 	     buf->size = buf->compat_size;		    \
   74:      if (buf->use != (size_t) buf->compat_use)		    \
   75:          if (buf->compat_use < INT_MAX)			    \
   76: 	     buf->use = buf->compat_use;
   77: 
   78: #else /* ! WITH_BUFFER_COMPAT */
   79: #define UPDATE_COMPAT(buf)
   80: #define CHECK_COMPAT(buf)
   81: #endif /* WITH_BUFFER_COMPAT */
   82: 
   83: /**
   84:  * xmlBufMemoryError:
   85:  * @extra:  extra informations
   86:  *
   87:  * Handle an out of memory condition
   88:  * To be improved...
   89:  */
   90: static void
   91: xmlBufMemoryError(xmlBufPtr buf, const char *extra)
   92: {
   93:     __xmlSimpleError(XML_FROM_BUFFER, XML_ERR_NO_MEMORY, NULL, NULL, extra);
   94:     if ((buf) && (buf->error == 0))
   95:         buf->error = XML_ERR_NO_MEMORY;
   96: }
   97: 
   98: /**
   99:  * xmlBufOverflowError:
  100:  * @extra:  extra informations
  101:  *
  102:  * Handle a buffer overflow error
  103:  * To be improved...
  104:  */
  105: static void
  106: xmlBufOverflowError(xmlBufPtr buf, const char *extra)
  107: {
  108:     __xmlSimpleError(XML_FROM_BUFFER, XML_BUF_OVERFLOW, NULL, NULL, extra);
  109:     if ((buf) && (buf->error == 0))
  110:         buf->error = XML_BUF_OVERFLOW;
  111: }
  112: 
  113: 
  114: /**
  115:  * xmlBufCreate:
  116:  *
  117:  * routine to create an XML buffer.
  118:  * returns the new structure.
  119:  */
  120: xmlBufPtr
  121: xmlBufCreate(void) {
  122:     xmlBufPtr ret;
  123: 
  124:     ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf));
  125:     if (ret == NULL) {
  126: 	xmlBufMemoryError(NULL, "creating buffer");
  127:         return(NULL);
  128:     }
  129:     ret->compat_use = 0;
  130:     ret->use = 0;
  131:     ret->error = 0;
  132:     ret->buffer = NULL;
  133:     ret->size = xmlDefaultBufferSize;
  134:     ret->compat_size = xmlDefaultBufferSize;
  135:     ret->alloc = xmlBufferAllocScheme;
  136:     ret->content = (xmlChar *) xmlMallocAtomic(ret->size * sizeof(xmlChar));
  137:     if (ret->content == NULL) {
  138: 	xmlBufMemoryError(ret, "creating buffer");
  139: 	xmlFree(ret);
  140:         return(NULL);
  141:     }
  142:     ret->content[0] = 0;
  143:     ret->contentIO = NULL;
  144:     return(ret);
  145: }
  146: 
  147: /**
  148:  * xmlBufCreateSize:
  149:  * @size: initial size of buffer
  150:  *
  151:  * routine to create an XML buffer.
  152:  * returns the new structure.
  153:  */
  154: xmlBufPtr
  155: xmlBufCreateSize(size_t size) {
  156:     xmlBufPtr ret;
  157: 
  158:     ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf));
  159:     if (ret == NULL) {
  160: 	xmlBufMemoryError(NULL, "creating buffer");
  161:         return(NULL);
  162:     }
  163:     ret->compat_use = 0;
  164:     ret->use = 0;
  165:     ret->error = 0;
  166:     ret->buffer = NULL;
  167:     ret->alloc = xmlBufferAllocScheme;
  168:     ret->size = (size ? size+2 : 0);         /* +1 for ending null */
  169:     ret->compat_size = (int) ret->size;
  170:     if (ret->size){
  171:         ret->content = (xmlChar *) xmlMallocAtomic(ret->size * sizeof(xmlChar));
  172:         if (ret->content == NULL) {
  173: 	    xmlBufMemoryError(ret, "creating buffer");
  174:             xmlFree(ret);
  175:             return(NULL);
  176:         }
  177:         ret->content[0] = 0;
  178:     } else
  179: 	ret->content = NULL;
  180:     ret->contentIO = NULL;
  181:     return(ret);
  182: }
  183: 
  184: /**
  185:  * xmlBufDetach:
  186:  * @buf:  the buffer
  187:  *
  188:  * Remove the string contained in a buffer and give it back to the
  189:  * caller. The buffer is reset to an empty content.
  190:  * This doesn't work with immutable buffers as they can't be reset.
  191:  *
  192:  * Returns the previous string contained by the buffer.
  193:  */
  194: xmlChar *
  195: xmlBufDetach(xmlBufPtr buf) {
  196:     xmlChar *ret;
  197: 
  198:     if (buf == NULL)
  199:         return(NULL);
  200:     if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE)
  201:         return(NULL);
  202:     if (buf->buffer != NULL)
  203:         return(NULL);
  204:     if (buf->error)
  205:         return(NULL);
  206: 
  207:     ret = buf->content;
  208:     buf->content = NULL;
  209:     buf->size = 0;
  210:     buf->use = 0;
  211:     buf->compat_use = 0;
  212:     buf->compat_size = 0;
  213: 
  214:     return ret;
  215: }
  216: 
  217: 
  218: /**
  219:  * xmlBufCreateStatic:
  220:  * @mem: the memory area
  221:  * @size:  the size in byte
  222:  *
  223:  * routine to create an XML buffer from an immutable memory area.
  224:  * The area won't be modified nor copied, and is expected to be
  225:  * present until the end of the buffer lifetime.
  226:  *
  227:  * returns the new structure.
  228:  */
  229: xmlBufPtr
  230: xmlBufCreateStatic(void *mem, size_t size) {
  231:     xmlBufPtr ret;
  232: 
  233:     if ((mem == NULL) || (size == 0))
  234:         return(NULL);
  235: 
  236:     ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf));
  237:     if (ret == NULL) {
  238: 	xmlBufMemoryError(NULL, "creating buffer");
  239:         return(NULL);
  240:     }
  241:     if (size < INT_MAX) {
  242:         ret->compat_use = size;
  243:         ret->compat_size = size;
  244:     } else {
  245:         ret->compat_use = INT_MAX;
  246:         ret->compat_size = INT_MAX;
  247:     }
  248:     ret->use = size;
  249:     ret->size = size;
  250:     ret->alloc = XML_BUFFER_ALLOC_IMMUTABLE;
  251:     ret->content = (xmlChar *) mem;
  252:     ret->error = 0;
  253:     ret->buffer = NULL;
  254:     return(ret);
  255: }
  256: 
  257: /**
  258:  * xmlBufGetAllocationScheme:
  259:  * @buf:  the buffer
  260:  *
  261:  * Get the buffer allocation scheme
  262:  *
  263:  * Returns the scheme or -1 in case of error
  264:  */
  265: int
  266: xmlBufGetAllocationScheme(xmlBufPtr buf) {
  267:     if (buf == NULL) {
  268: #ifdef DEBUG_BUFFER
  269:         xmlGenericError(xmlGenericErrorContext,
  270: 		"xmlBufGetAllocationScheme: buf == NULL\n");
  271: #endif
  272:         return(-1);
  273:     }
  274:     return(buf->alloc);
  275: }
  276: 
  277: /**
  278:  * xmlBufSetAllocationScheme:
  279:  * @buf:  the buffer to tune
  280:  * @scheme:  allocation scheme to use
  281:  *
  282:  * Sets the allocation scheme for this buffer
  283:  *
  284:  * returns 0 in case of success and -1 in case of failure
  285:  */
  286: int
  287: xmlBufSetAllocationScheme(xmlBufPtr buf,
  288:                           xmlBufferAllocationScheme scheme) {
  289:     if ((buf == NULL) || (buf->error != 0)) {
  290: #ifdef DEBUG_BUFFER
  291:         xmlGenericError(xmlGenericErrorContext,
  292: 		"xmlBufSetAllocationScheme: buf == NULL or in error\n");
  293: #endif
  294:         return(-1);
  295:     }
  296:     if ((buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) ||
  297:         (buf->alloc == XML_BUFFER_ALLOC_IO))
  298:         return(-1);
  299:     if ((scheme == XML_BUFFER_ALLOC_DOUBLEIT) ||
  300:         (scheme == XML_BUFFER_ALLOC_EXACT) ||
  301:         (scheme == XML_BUFFER_ALLOC_HYBRID) ||
  302:         (scheme == XML_BUFFER_ALLOC_IMMUTABLE)) {
  303: 	buf->alloc = scheme;
  304:         if (buf->buffer)
  305:             buf->buffer->alloc = scheme;
  306:         return(0);
  307:     }
  308:     /*
  309:      * Switching a buffer ALLOC_IO has the side effect of initializing
  310:      * the contentIO field with the current content
  311:      */
  312:     if (scheme == XML_BUFFER_ALLOC_IO) {
  313:         buf->alloc = XML_BUFFER_ALLOC_IO;
  314:         buf->contentIO = buf->content;
  315:     }
  316:     return(-1);
  317: }
  318: 
  319: /**
  320:  * xmlBufFree:
  321:  * @buf:  the buffer to free
  322:  *
  323:  * Frees an XML buffer. It frees both the content and the structure which
  324:  * encapsulate it.
  325:  */
  326: void
  327: xmlBufFree(xmlBufPtr buf) {
  328:     if (buf == NULL) {
  329: #ifdef DEBUG_BUFFER
  330:         xmlGenericError(xmlGenericErrorContext,
  331: 		"xmlBufFree: buf == NULL\n");
  332: #endif
  333: 	return;
  334:     }
  335: 
  336:     if ((buf->alloc == XML_BUFFER_ALLOC_IO) &&
  337:         (buf->contentIO != NULL)) {
  338:         xmlFree(buf->contentIO);
  339:     } else if ((buf->content != NULL) &&
  340:         (buf->alloc != XML_BUFFER_ALLOC_IMMUTABLE)) {
  341:         xmlFree(buf->content);
  342:     }
  343:     xmlFree(buf);
  344: }
  345: 
  346: /**
  347:  * xmlBufEmpty:
  348:  * @buf:  the buffer
  349:  *
  350:  * empty a buffer.
  351:  */
  352: void
  353: xmlBufEmpty(xmlBufPtr buf) {
  354:     if ((buf == NULL) || (buf->error != 0)) return;
  355:     if (buf->content == NULL) return;
  356:     CHECK_COMPAT(buf)
  357:     buf->use = 0;
  358:     if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) {
  359:         buf->content = BAD_CAST "";
  360:     } else if ((buf->alloc == XML_BUFFER_ALLOC_IO) &&
  361:                (buf->contentIO != NULL)) {
  362:         size_t start_buf = buf->content - buf->contentIO;
  363: 
  364: 	buf->size += start_buf;
  365:         buf->content = buf->contentIO;
  366:         buf->content[0] = 0;
  367:     } else {
  368:         buf->content[0] = 0;
  369:     }
  370:     UPDATE_COMPAT(buf)
  371: }
  372: 
  373: /**
  374:  * xmlBufShrink:
  375:  * @buf:  the buffer to dump
  376:  * @len:  the number of xmlChar to remove
  377:  *
  378:  * Remove the beginning of an XML buffer.
  379:  * NOTE that this routine behaviour differs from xmlBufferShrink()
  380:  * as it will return 0 on error instead of -1 due to size_t being
  381:  * used as the return type.
  382:  *
  383:  * Returns the number of byte removed or 0 in case of failure
  384:  */
  385: size_t
  386: xmlBufShrink(xmlBufPtr buf, size_t len) {
  387:     if ((buf == NULL) || (buf->error != 0)) return(0);
  388:     CHECK_COMPAT(buf)
  389:     if (len == 0) return(0);
  390:     if (len > buf->use) return(0);
  391: 
  392:     buf->use -= len;
  393:     if ((buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) ||
  394:         ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL))) {
  395: 	/*
  396: 	 * we just move the content pointer, but also make sure
  397: 	 * the perceived buffer size has shrinked accordingly
  398: 	 */
  399:         buf->content += len;
  400: 	buf->size -= len;
  401: 
  402:         /*
  403: 	 * sometimes though it maybe be better to really shrink
  404: 	 * on IO buffers
  405: 	 */
  406: 	if ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL)) {
  407: 	    size_t start_buf = buf->content - buf->contentIO;
  408: 	    if (start_buf >= buf->size) {
  409: 		memmove(buf->contentIO, &buf->content[0], buf->use);
  410: 		buf->content = buf->contentIO;
  411: 		buf->content[buf->use] = 0;
  412: 		buf->size += start_buf;
  413: 	    }
  414: 	}
  415:     } else {
  416: 	memmove(buf->content, &buf->content[len], buf->use);
  417: 	buf->content[buf->use] = 0;
  418:     }
  419:     UPDATE_COMPAT(buf)
  420:     return(len);
  421: }
  422: 
  423: /**
  424:  * xmlBufGrowInternal:
  425:  * @buf:  the buffer
  426:  * @len:  the minimum free size to allocate
  427:  *
  428:  * Grow the available space of an XML buffer, @len is the target value
  429:  * Error checking should be done on buf->error since using the return
  430:  * value doesn't work that well
  431:  *
  432:  * Returns 0 in case of error or the length made available otherwise
  433:  */
  434: static size_t
  435: xmlBufGrowInternal(xmlBufPtr buf, size_t len) {
  436:     size_t size;
  437:     xmlChar *newbuf;
  438: 
  439:     if ((buf == NULL) || (buf->error != 0)) return(0);
  440:     CHECK_COMPAT(buf)
  441: 
  442:     if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return(0);
  443:     if (buf->use + len < buf->size)
  444:         return(buf->size - buf->use);
  445: 
  446:     /*
  447:      * Windows has a BIG problem on realloc timing, so we try to double
  448:      * the buffer size (if that's enough) (bug 146697)
  449:      * Apparently BSD too, and it's probably best for linux too
  450:      * On an embedded system this may be something to change
  451:      */
  452: #if 1
  453:     if (buf->size > (size_t) len)
  454:         size = buf->size * 2;
  455:     else
  456:         size = buf->use + len + 100;
  457: #else
  458:     size = buf->use + len + 100;
  459: #endif
  460: 
  461:     if ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL)) {
  462:         size_t start_buf = buf->content - buf->contentIO;
  463: 
  464: 	newbuf = (xmlChar *) xmlRealloc(buf->contentIO, start_buf + size);
  465: 	if (newbuf == NULL) {
  466: 	    xmlBufMemoryError(buf, "growing buffer");
  467: 	    return(0);
  468: 	}
  469: 	buf->contentIO = newbuf;
  470: 	buf->content = newbuf + start_buf;
  471:     } else {
  472: 	newbuf = (xmlChar *) xmlRealloc(buf->content, size);
  473: 	if (newbuf == NULL) {
  474: 	    xmlBufMemoryError(buf, "growing buffer");
  475: 	    return(0);
  476: 	}
  477: 	buf->content = newbuf;
  478:     }
  479:     buf->size = size;
  480:     UPDATE_COMPAT(buf)
  481:     return(buf->size - buf->use);
  482: }
  483: 
  484: /**
  485:  * xmlBufGrow:
  486:  * @buf:  the buffer
  487:  * @len:  the minimum free size to allocate
  488:  *
  489:  * Grow the available space of an XML buffer, @len is the target value
  490:  * This is been kept compatible with xmlBufferGrow() as much as possible
  491:  *
  492:  * Returns -1 in case of error or the length made available otherwise
  493:  */
  494: int
  495: xmlBufGrow(xmlBufPtr buf, int len) {
  496:     size_t ret;
  497: 
  498:     if ((buf == NULL) || (len < 0)) return(-1);
  499:     if (len == 0)
  500:         return(0);
  501:     ret = xmlBufGrowInternal(buf, len);
  502:     if (buf->error != 0)
  503:         return(-1);
  504:     return((int) ret);
  505: }
  506: 
  507: /**
  508:  * xmlBufInflate:
  509:  * @buf:  the buffer
  510:  * @len:  the minimum extra free size to allocate
  511:  *
  512:  * Grow the available space of an XML buffer, adding at least @len bytes
  513:  *
  514:  * Returns 0 if successful or -1 in case of error
  515:  */
  516: int
  517: xmlBufInflate(xmlBufPtr buf, size_t len) {
  518:     if (buf == NULL) return(-1);
  519:     xmlBufGrowInternal(buf, len + buf->size);
  520:     if (buf->error)
  521:         return(-1);
  522:     return(0);
  523: }
  524: 
  525: /**
  526:  * xmlBufDump:
  527:  * @file:  the file output
  528:  * @buf:  the buffer to dump
  529:  *
  530:  * Dumps an XML buffer to  a FILE *.
  531:  * Returns the number of #xmlChar written
  532:  */
  533: size_t
  534: xmlBufDump(FILE *file, xmlBufPtr buf) {
  535:     size_t ret;
  536: 
  537:     if ((buf == NULL) || (buf->error != 0)) {
  538: #ifdef DEBUG_BUFFER
  539:         xmlGenericError(xmlGenericErrorContext,
  540: 		"xmlBufDump: buf == NULL or in error\n");
  541: #endif
  542: 	return(0);
  543:     }
  544:     if (buf->content == NULL) {
  545: #ifdef DEBUG_BUFFER
  546:         xmlGenericError(xmlGenericErrorContext,
  547: 		"xmlBufDump: buf->content == NULL\n");
  548: #endif
  549: 	return(0);
  550:     }
  551:     CHECK_COMPAT(buf)
  552:     if (file == NULL)
  553: 	file = stdout;
  554:     ret = fwrite(buf->content, sizeof(xmlChar), buf->use, file);
  555:     return(ret);
  556: }
  557: 
  558: /**
  559:  * xmlBufContent:
  560:  * @buf:  the buffer
  561:  *
  562:  * Function to extract the content of a buffer
  563:  *
  564:  * Returns the internal content
  565:  */
  566: 
  567: xmlChar *
  568: xmlBufContent(const xmlBufPtr buf)
  569: {
  570:     if ((!buf) || (buf->error))
  571:         return NULL;
  572: 
  573:     return(buf->content);
  574: }
  575: 
  576: /**
  577:  * xmlBufEnd:
  578:  * @buf:  the buffer
  579:  *
  580:  * Function to extract the end of the content of a buffer
  581:  *
  582:  * Returns the end of the internal content or NULL in case of error
  583:  */
  584: 
  585: xmlChar *
  586: xmlBufEnd(const xmlBufPtr buf)
  587: {
  588:     if ((!buf) || (buf->error))
  589:         return NULL;
  590:     CHECK_COMPAT(buf)
  591: 
  592:     return(&buf->content[buf->use]);
  593: }
  594: 
  595: /**
  596:  * xmlBufAddLen:
  597:  * @buf:  the buffer
  598:  * @len:  the size which were added at the end
  599:  *
  600:  * Sometime data may be added at the end of the buffer without
  601:  * using the xmlBuf APIs that is used to expand the used space
  602:  * and set the zero terminating at the end of the buffer
  603:  *
  604:  * Returns -1 in case of error and 0 otherwise
  605:  */
  606: int
  607: xmlBufAddLen(xmlBufPtr buf, size_t len) {
  608:     if ((buf == NULL) || (buf->error))
  609:         return(-1);
  610:     CHECK_COMPAT(buf)
  611:     if (len > (buf->size - buf->use))
  612:         return(-1);
  613:     buf->use += len;
  614:     UPDATE_COMPAT(buf)
  615:     if (buf->size > buf->use)
  616:         buf->content[buf->use] = 0;
  617:     else
  618:         return(-1);
  619:     return(0);
  620: }
  621: 
  622: /**
  623:  * xmlBufErase:
  624:  * @buf:  the buffer
  625:  * @len:  the size to erase at the end
  626:  *
  627:  * Sometime data need to be erased at the end of the buffer
  628:  *
  629:  * Returns -1 in case of error and 0 otherwise
  630:  */
  631: int
  632: xmlBufErase(xmlBufPtr buf, size_t len) {
  633:     if ((buf == NULL) || (buf->error))
  634:         return(-1);
  635:     CHECK_COMPAT(buf)
  636:     if (len > buf->use)
  637:         return(-1);
  638:     buf->use -= len;
  639:     buf->content[buf->use] = 0;
  640:     UPDATE_COMPAT(buf)
  641:     return(0);
  642: }
  643: 
  644: /**
  645:  * xmlBufLength:
  646:  * @buf:  the buffer
  647:  *
  648:  * Function to get the length of a buffer
  649:  *
  650:  * Returns the length of data in the internal content
  651:  */
  652: 
  653: size_t
  654: xmlBufLength(const xmlBufPtr buf)
  655: {
  656:     if ((!buf) || (buf->error))
  657:         return 0;
  658:     CHECK_COMPAT(buf)
  659: 
  660:     return(buf->use);
  661: }
  662: 
  663: /**
  664:  * xmlBufUse:
  665:  * @buf:  the buffer
  666:  *
  667:  * Function to get the length of a buffer
  668:  *
  669:  * Returns the length of data in the internal content
  670:  */
  671: 
  672: size_t
  673: xmlBufUse(const xmlBufPtr buf)
  674: {
  675:     if ((!buf) || (buf->error))
  676:         return 0;
  677:     CHECK_COMPAT(buf)
  678: 
  679:     return(buf->use);
  680: }
  681: 
  682: /**
  683:  * xmlBufAvail:
  684:  * @buf:  the buffer
  685:  *
  686:  * Function to find how much free space is allocated but not
  687:  * used in the buffer. It does not account for the terminating zero
  688:  * usually needed
  689:  *
  690:  * Returns the amount or 0 if none or an error occured
  691:  */
  692: 
  693: size_t
  694: xmlBufAvail(const xmlBufPtr buf)
  695: {
  696:     if ((!buf) || (buf->error))
  697:         return 0;
  698:     CHECK_COMPAT(buf)
  699: 
  700:     return(buf->size - buf->use);
  701: }
  702: 
  703: /**
  704:  * xmlBufIsEmpty:
  705:  * @buf:  the buffer
  706:  *
  707:  * Tell if a buffer is empty
  708:  *
  709:  * Returns 0 if no, 1 if yes and -1 in case of error
  710:  */
  711: int
  712: xmlBufIsEmpty(const xmlBufPtr buf)
  713: {
  714:     if ((!buf) || (buf->error))
  715:         return(-1);
  716:     CHECK_COMPAT(buf)
  717: 
  718:     return(buf->use == 0);
  719: }
  720: 
  721: /**
  722:  * xmlBufResize:
  723:  * @buf:  the buffer to resize
  724:  * @size:  the desired size
  725:  *
  726:  * Resize a buffer to accommodate minimum size of @size.
  727:  *
  728:  * Returns  0 in case of problems, 1 otherwise
  729:  */
  730: int
  731: xmlBufResize(xmlBufPtr buf, size_t size)
  732: {
  733:     unsigned int newSize;
  734:     xmlChar* rebuf = NULL;
  735:     size_t start_buf;
  736: 
  737:     if ((buf == NULL) || (buf->error))
  738:         return(0);
  739:     CHECK_COMPAT(buf)
  740: 
  741:     if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return(0);
  742: 
  743:     /* Don't resize if we don't have to */
  744:     if (size < buf->size)
  745:         return 1;
  746: 
  747:     /* figure out new size */
  748:     switch (buf->alloc){
  749: 	case XML_BUFFER_ALLOC_IO:
  750: 	case XML_BUFFER_ALLOC_DOUBLEIT:
  751: 	    /*take care of empty case*/
  752: 	    newSize = (buf->size ? buf->size*2 : size + 10);
  753: 	    while (size > newSize) {
  754: 	        if (newSize > UINT_MAX / 2) {
  755: 	            xmlBufMemoryError(buf, "growing buffer");
  756: 	            return 0;
  757: 	        }
  758: 	        newSize *= 2;
  759: 	    }
  760: 	    break;
  761: 	case XML_BUFFER_ALLOC_EXACT:
  762: 	    newSize = size+10;
  763: 	    break;
  764:         case XML_BUFFER_ALLOC_HYBRID:
  765:             if (buf->use < BASE_BUFFER_SIZE)
  766:                 newSize = size;
  767:             else {
  768:                 newSize = buf->size * 2;
  769:                 while (size > newSize) {
  770:                     if (newSize > UINT_MAX / 2) {
  771:                         xmlBufMemoryError(buf, "growing buffer");
  772:                         return 0;
  773:                     }
  774:                     newSize *= 2;
  775:                 }
  776:             }
  777:             break;
  778: 
  779: 	default:
  780: 	    newSize = size+10;
  781: 	    break;
  782:     }
  783: 
  784:     if ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL)) {
  785:         start_buf = buf->content - buf->contentIO;
  786: 
  787:         if (start_buf > newSize) {
  788: 	    /* move data back to start */
  789: 	    memmove(buf->contentIO, buf->content, buf->use);
  790: 	    buf->content = buf->contentIO;
  791: 	    buf->content[buf->use] = 0;
  792: 	    buf->size += start_buf;
  793: 	} else {
  794: 	    rebuf = (xmlChar *) xmlRealloc(buf->contentIO, start_buf + newSize);
  795: 	    if (rebuf == NULL) {
  796: 		xmlBufMemoryError(buf, "growing buffer");
  797: 		return 0;
  798: 	    }
  799: 	    buf->contentIO = rebuf;
  800: 	    buf->content = rebuf + start_buf;
  801: 	}
  802:     } else {
  803: 	if (buf->content == NULL) {
  804: 	    rebuf = (xmlChar *) xmlMallocAtomic(newSize);
  805: 	} else if (buf->size - buf->use < 100) {
  806: 	    rebuf = (xmlChar *) xmlRealloc(buf->content, newSize);
  807:         } else {
  808: 	    /*
  809: 	     * if we are reallocating a buffer far from being full, it's
  810: 	     * better to make a new allocation and copy only the used range
  811: 	     * and free the old one.
  812: 	     */
  813: 	    rebuf = (xmlChar *) xmlMallocAtomic(newSize);
  814: 	    if (rebuf != NULL) {
  815: 		memcpy(rebuf, buf->content, buf->use);
  816: 		xmlFree(buf->content);
  817: 		rebuf[buf->use] = 0;
  818: 	    }
  819: 	}
  820: 	if (rebuf == NULL) {
  821: 	    xmlBufMemoryError(buf, "growing buffer");
  822: 	    return 0;
  823: 	}
  824: 	buf->content = rebuf;
  825:     }
  826:     buf->size = newSize;
  827:     UPDATE_COMPAT(buf)
  828: 
  829:     return 1;
  830: }
  831: 
  832: /**
  833:  * xmlBufAdd:
  834:  * @buf:  the buffer to dump
  835:  * @str:  the #xmlChar string
  836:  * @len:  the number of #xmlChar to add
  837:  *
  838:  * Add a string range to an XML buffer. if len == -1, the length of
  839:  * str is recomputed.
  840:  *
  841:  * Returns 0 successful, a positive error code number otherwise
  842:  *         and -1 in case of internal or API error.
  843:  */
  844: int
  845: xmlBufAdd(xmlBufPtr buf, const xmlChar *str, int len) {
  846:     unsigned int needSize;
  847: 
  848:     if ((str == NULL) || (buf == NULL) || (buf->error))
  849: 	return -1;
  850:     CHECK_COMPAT(buf)
  851: 
  852:     if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return -1;
  853:     if (len < -1) {
  854: #ifdef DEBUG_BUFFER
  855:         xmlGenericError(xmlGenericErrorContext,
  856: 		"xmlBufAdd: len < 0\n");
  857: #endif
  858: 	return -1;
  859:     }
  860:     if (len == 0) return 0;
  861: 
  862:     if (len < 0)
  863:         len = xmlStrlen(str);
  864: 
  865:     if (len < 0) return -1;
  866:     if (len == 0) return 0;
  867: 
  868:     needSize = buf->use + len + 2;
  869:     if (needSize > buf->size){
  870:         if (!xmlBufResize(buf, needSize)){
  871: 	    xmlBufMemoryError(buf, "growing buffer");
  872:             return XML_ERR_NO_MEMORY;
  873:         }
  874:     }
  875: 
  876:     memmove(&buf->content[buf->use], str, len*sizeof(xmlChar));
  877:     buf->use += len;
  878:     buf->content[buf->use] = 0;
  879:     UPDATE_COMPAT(buf)
  880:     return 0;
  881: }
  882: 
  883: /**
  884:  * xmlBufAddHead:
  885:  * @buf:  the buffer
  886:  * @str:  the #xmlChar string
  887:  * @len:  the number of #xmlChar to add
  888:  *
  889:  * Add a string range to the beginning of an XML buffer.
  890:  * if len == -1, the length of @str is recomputed.
  891:  *
  892:  * Returns 0 successful, a positive error code number otherwise
  893:  *         and -1 in case of internal or API error.
  894:  */
  895: int
  896: xmlBufAddHead(xmlBufPtr buf, const xmlChar *str, int len) {
  897:     unsigned int needSize;
  898: 
  899:     if ((buf == NULL) || (buf->error))
  900:         return(-1);
  901:     CHECK_COMPAT(buf)
  902:     if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return -1;
  903:     if (str == NULL) {
  904: #ifdef DEBUG_BUFFER
  905:         xmlGenericError(xmlGenericErrorContext,
  906: 		"xmlBufAddHead: str == NULL\n");
  907: #endif
  908: 	return -1;
  909:     }
  910:     if (len < -1) {
  911: #ifdef DEBUG_BUFFER
  912:         xmlGenericError(xmlGenericErrorContext,
  913: 		"xmlBufAddHead: len < 0\n");
  914: #endif
  915: 	return -1;
  916:     }
  917:     if (len == 0) return 0;
  918: 
  919:     if (len < 0)
  920:         len = xmlStrlen(str);
  921: 
  922:     if (len <= 0) return -1;
  923: 
  924:     if ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL)) {
  925:         size_t start_buf = buf->content - buf->contentIO;
  926: 
  927: 	if (start_buf > (unsigned int) len) {
  928: 	    /*
  929: 	     * We can add it in the space previously shrinked
  930: 	     */
  931: 	    buf->content -= len;
  932:             memmove(&buf->content[0], str, len);
  933: 	    buf->use += len;
  934: 	    buf->size += len;
  935: 	    UPDATE_COMPAT(buf)
  936: 	    return(0);
  937: 	}
  938:     }
  939:     needSize = buf->use + len + 2;
  940:     if (needSize > buf->size){
  941:         if (!xmlBufResize(buf, needSize)){
  942: 	    xmlBufMemoryError(buf, "growing buffer");
  943:             return XML_ERR_NO_MEMORY;
  944:         }
  945:     }
  946: 
  947:     memmove(&buf->content[len], &buf->content[0], buf->use);
  948:     memmove(&buf->content[0], str, len);
  949:     buf->use += len;
  950:     buf->content[buf->use] = 0;
  951:     UPDATE_COMPAT(buf)
  952:     return 0;
  953: }
  954: 
  955: /**
  956:  * xmlBufCat:
  957:  * @buf:  the buffer to add to
  958:  * @str:  the #xmlChar string
  959:  *
  960:  * Append a zero terminated string to an XML buffer.
  961:  *
  962:  * Returns 0 successful, a positive error code number otherwise
  963:  *         and -1 in case of internal or API error.
  964:  */
  965: int
  966: xmlBufCat(xmlBufPtr buf, const xmlChar *str) {
  967:     if ((buf == NULL) || (buf->error))
  968:         return(-1);
  969:     CHECK_COMPAT(buf)
  970:     if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return -1;
  971:     if (str == NULL) return -1;
  972:     return xmlBufAdd(buf, str, -1);
  973: }
  974: 
  975: /**
  976:  * xmlBufCCat:
  977:  * @buf:  the buffer to dump
  978:  * @str:  the C char string
  979:  *
  980:  * Append a zero terminated C string to an XML buffer.
  981:  *
  982:  * Returns 0 successful, a positive error code number otherwise
  983:  *         and -1 in case of internal or API error.
  984:  */
  985: int
  986: xmlBufCCat(xmlBufPtr buf, const char *str) {
  987:     const char *cur;
  988: 
  989:     if ((buf == NULL) || (buf->error))
  990:         return(-1);
  991:     CHECK_COMPAT(buf)
  992:     if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return -1;
  993:     if (str == NULL) {
  994: #ifdef DEBUG_BUFFER
  995:         xmlGenericError(xmlGenericErrorContext,
  996: 		"xmlBufCCat: str == NULL\n");
  997: #endif
  998: 	return -1;
  999:     }
 1000:     for (cur = str;*cur != 0;cur++) {
 1001:         if (buf->use  + 10 >= buf->size) {
 1002:             if (!xmlBufResize(buf, buf->use+10)){
 1003: 		xmlBufMemoryError(buf, "growing buffer");
 1004:                 return XML_ERR_NO_MEMORY;
 1005:             }
 1006:         }
 1007:         buf->content[buf->use++] = *cur;
 1008:     }
 1009:     buf->content[buf->use] = 0;
 1010:     UPDATE_COMPAT(buf)
 1011:     return 0;
 1012: }
 1013: 
 1014: /**
 1015:  * xmlBufWriteCHAR:
 1016:  * @buf:  the XML buffer
 1017:  * @string:  the string to add
 1018:  *
 1019:  * routine which manages and grows an output buffer. This one adds
 1020:  * xmlChars at the end of the buffer.
 1021:  *
 1022:  * Returns 0 if successful, a positive error code number otherwise
 1023:  *         and -1 in case of internal or API error.
 1024:  */
 1025: int
 1026: xmlBufWriteCHAR(xmlBufPtr buf, const xmlChar *string) {
 1027:     if ((buf == NULL) || (buf->error))
 1028:         return(-1);
 1029:     CHECK_COMPAT(buf)
 1030:     if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE)
 1031:         return(-1);
 1032:     return(xmlBufCat(buf, string));
 1033: }
 1034: 
 1035: /**
 1036:  * xmlBufWriteChar:
 1037:  * @buf:  the XML buffer output
 1038:  * @string:  the string to add
 1039:  *
 1040:  * routine which manage and grows an output buffer. This one add
 1041:  * C chars at the end of the array.
 1042:  *
 1043:  * Returns 0 if successful, a positive error code number otherwise
 1044:  *         and -1 in case of internal or API error.
 1045:  */
 1046: int
 1047: xmlBufWriteChar(xmlBufPtr buf, const char *string) {
 1048:     if ((buf == NULL) || (buf->error))
 1049:         return(-1);
 1050:     CHECK_COMPAT(buf)
 1051:     if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE)
 1052:         return(-1);
 1053:     return(xmlBufCCat(buf, string));
 1054: }
 1055: 
 1056: 
 1057: /**
 1058:  * xmlBufWriteQuotedString:
 1059:  * @buf:  the XML buffer output
 1060:  * @string:  the string to add
 1061:  *
 1062:  * routine which manage and grows an output buffer. This one writes
 1063:  * a quoted or double quoted #xmlChar string, checking first if it holds
 1064:  * quote or double-quotes internally
 1065:  *
 1066:  * Returns 0 if successful, a positive error code number otherwise
 1067:  *         and -1 in case of internal or API error.
 1068:  */
 1069: int
 1070: xmlBufWriteQuotedString(xmlBufPtr buf, const xmlChar *string) {
 1071:     const xmlChar *cur, *base;
 1072:     if ((buf == NULL) || (buf->error))
 1073:         return(-1);
 1074:     CHECK_COMPAT(buf)
 1075:     if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE)
 1076:         return(-1);
 1077:     if (xmlStrchr(string, '\"')) {
 1078:         if (xmlStrchr(string, '\'')) {
 1079: #ifdef DEBUG_BUFFER
 1080: 	    xmlGenericError(xmlGenericErrorContext,
 1081:  "xmlBufWriteQuotedString: string contains quote and double-quotes !\n");
 1082: #endif
 1083: 	    xmlBufCCat(buf, "\"");
 1084:             base = cur = string;
 1085:             while(*cur != 0){
 1086:                 if(*cur == '"'){
 1087:                     if (base != cur)
 1088:                         xmlBufAdd(buf, base, cur - base);
 1089:                     xmlBufAdd(buf, BAD_CAST "&quot;", 6);
 1090:                     cur++;
 1091:                     base = cur;
 1092:                 }
 1093:                 else {
 1094:                     cur++;
 1095:                 }
 1096:             }
 1097:             if (base != cur)
 1098:                 xmlBufAdd(buf, base, cur - base);
 1099: 	    xmlBufCCat(buf, "\"");
 1100: 	}
 1101:         else{
 1102: 	    xmlBufCCat(buf, "\'");
 1103:             xmlBufCat(buf, string);
 1104: 	    xmlBufCCat(buf, "\'");
 1105:         }
 1106:     } else {
 1107:         xmlBufCCat(buf, "\"");
 1108:         xmlBufCat(buf, string);
 1109:         xmlBufCCat(buf, "\"");
 1110:     }
 1111:     return(0);
 1112: }
 1113: 
 1114: /**
 1115:  * xmlBufFromBuffer:
 1116:  * @buffer: incoming old buffer to convert to a new one
 1117:  *
 1118:  * Helper routine to switch from the old buffer structures in use
 1119:  * in various APIs. It creates a wrapper xmlBufPtr which will be
 1120:  * used for internal processing until the xmlBufBackToBuffer() is
 1121:  * issued.
 1122:  *
 1123:  * Returns a new xmlBufPtr unless the call failed and NULL is returned
 1124:  */
 1125: xmlBufPtr
 1126: xmlBufFromBuffer(xmlBufferPtr buffer) {
 1127:     xmlBufPtr ret;
 1128: 
 1129:     if (buffer == NULL)
 1130:         return(NULL);
 1131: 
 1132:     ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf));
 1133:     if (ret == NULL) {
 1134: 	xmlBufMemoryError(NULL, "creating buffer");
 1135:         return(NULL);
 1136:     }
 1137:     ret->use = buffer->use;
 1138:     ret->size = buffer->size;
 1139:     ret->compat_use = buffer->use;
 1140:     ret->compat_size = buffer->size;
 1141:     ret->error = 0;
 1142:     ret->buffer = buffer;
 1143:     ret->alloc = buffer->alloc;
 1144:     ret->content = buffer->content;
 1145:     ret->contentIO = buffer->contentIO;
 1146: 
 1147:     return(ret);
 1148: }
 1149: 
 1150: /**
 1151:  * xmlBufBackToBuffer:
 1152:  * @buf: new buffer wrapping the old one
 1153:  *
 1154:  * Function to be called once internal processing had been done to
 1155:  * update back the buffer provided by the user. This can lead to
 1156:  * a failure in case the size accumulated in the xmlBuf is larger
 1157:  * than what an xmlBuffer can support on 64 bits (INT_MAX)
 1158:  * The xmlBufPtr @buf wrapper is deallocated by this call in any case.
 1159:  *
 1160:  * Returns the old xmlBufferPtr unless the call failed and NULL is returned
 1161:  */
 1162: xmlBufferPtr
 1163: xmlBufBackToBuffer(xmlBufPtr buf) {
 1164:     xmlBufferPtr ret;
 1165: 
 1166:     if ((buf == NULL) || (buf->error))
 1167:         return(NULL);
 1168:     CHECK_COMPAT(buf)
 1169:     if (buf->buffer == NULL) {
 1170:         xmlBufFree(buf);
 1171:         return(NULL);
 1172:     }
 1173: 
 1174:     ret = buf->buffer;
 1175:     /*
 1176:      * What to do in case of error in the buffer ???
 1177:      */
 1178:     if (buf->use > INT_MAX) {
 1179:         /*
 1180:          * Worse case, we really allocated and used more than the
 1181:          * maximum allowed memory for an xmlBuffer on this architecture.
 1182:          * Keep the buffer but provide a truncated size value.
 1183:          */
 1184:         xmlBufOverflowError(buf, "Used size too big for xmlBuffer");
 1185:         ret->use = INT_MAX;
 1186:         ret->size = INT_MAX;
 1187:     } else if (buf->size > INT_MAX) {
 1188:         /*
 1189:          * milder case, we allocated more than the maximum allowed memory
 1190:          * for an xmlBuffer on this architecture, but used less than the
 1191:          * limit.
 1192:          * Keep the buffer but provide a truncated size value.
 1193:          */
 1194:         xmlBufOverflowError(buf, "Allocated size too big for xmlBuffer");
 1195:         ret->size = INT_MAX;
 1196:     }
 1197:     ret->use = (int) buf->use;
 1198:     ret->size = (int) buf->size;
 1199:     ret->alloc = buf->alloc;
 1200:     ret->content = buf->content;
 1201:     ret->contentIO = buf->contentIO;
 1202:     xmlFree(buf);
 1203:     return(ret);
 1204: }
 1205: 
 1206: /**
 1207:  * xmlBufMergeBuffer:
 1208:  * @buf: an xmlBufPtr
 1209:  * @buffer: the buffer to consume into @buf
 1210:  *
 1211:  * The content of @buffer is appended to @buf and @buffer is freed
 1212:  *
 1213:  * Returns -1 in case of error, 0 otherwise, in any case @buffer is freed
 1214:  */
 1215: int
 1216: xmlBufMergeBuffer(xmlBufPtr buf, xmlBufferPtr buffer) {
 1217:     int ret = 0;
 1218: 
 1219:     if ((buf == NULL) || (buf->error)) {
 1220: 	xmlBufferFree(buffer);
 1221:         return(-1);
 1222:     }
 1223:     CHECK_COMPAT(buf)
 1224:     if ((buffer != NULL) && (buffer->content != NULL) &&
 1225:              (buffer->use > 0)) {
 1226:         ret = xmlBufAdd(buf, buffer->content, buffer->use);
 1227:     }
 1228:     xmlBufferFree(buffer);
 1229:     return(ret);
 1230: }
 1231: 
 1232: /**
 1233:  * xmlBufResetInput:
 1234:  * @buf: an xmlBufPtr
 1235:  * @input: an xmlParserInputPtr
 1236:  *
 1237:  * Update the input to use the current set of pointers from the buffer.
 1238:  *
 1239:  * Returns -1 in case of error, 0 otherwise
 1240:  */
 1241: int
 1242: xmlBufResetInput(xmlBufPtr buf, xmlParserInputPtr input) {
 1243:     if ((input == NULL) || (buf == NULL) || (buf->error))
 1244:         return(-1);
 1245:     CHECK_COMPAT(buf)
 1246:     input->base = input->cur = buf->content;
 1247:     input->end = &buf->content[buf->use];
 1248:     return(0);
 1249: }
 1250: 
 1251: /**
 1252:  * xmlBufGetInputBase:
 1253:  * @buf: an xmlBufPtr
 1254:  * @input: an xmlParserInputPtr
 1255:  *
 1256:  * Get the base of the @input relative to the beginning of the buffer
 1257:  *
 1258:  * Returns the size_t corresponding to the displacement
 1259:  */
 1260: size_t
 1261: xmlBufGetInputBase(xmlBufPtr buf, xmlParserInputPtr input) {
 1262:     size_t base;
 1263: 
 1264:     if ((input == NULL) || (buf == NULL) || (buf->error))
 1265:         return(-1);
 1266:     CHECK_COMPAT(buf)
 1267:     base = input->base - buf->content;
 1268:     /*
 1269:      * We could do some pointer arythmetic checks but that's probably
 1270:      * sufficient.
 1271:      */
 1272:     if (base > buf->size) {
 1273:         xmlBufOverflowError(buf, "Input reference outside of the buffer");
 1274:         base = 0;
 1275:     }
 1276:     return(base);
 1277: }
 1278: 
 1279: /**
 1280:  * xmlBufSetInputBaseCur:
 1281:  * @buf: an xmlBufPtr
 1282:  * @input: an xmlParserInputPtr
 1283:  * @base: the base value relative to the beginning of the buffer
 1284:  * @cur: the cur value relative to the beginning of the buffer
 1285:  *
 1286:  * Update the input to use the base and cur relative to the buffer
 1287:  * after a possible reallocation of its content
 1288:  *
 1289:  * Returns -1 in case of error, 0 otherwise
 1290:  */
 1291: int
 1292: xmlBufSetInputBaseCur(xmlBufPtr buf, xmlParserInputPtr input,
 1293:                       size_t base, size_t cur) {
 1294:     if ((input == NULL) || (buf == NULL) || (buf->error))
 1295:         return(-1);
 1296:     CHECK_COMPAT(buf)
 1297:     input->base = &buf->content[base];
 1298:     input->cur = input->base + cur;
 1299:     input->end = &buf->content[buf->use];
 1300:     return(0);
 1301: }
 1302: 
 1303: #define bottom_buf
 1304: #include "elfgcchack.h"

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