Home | History | Annotate | Download | only in cups
      1 /*
      2  * File functions for CUPS.
      3  *
      4  * Since stdio files max out at 256 files on many systems, we have to
      5  * write similar functions without this limit.  At the same time, using
      6  * our own file functions allows us to provide transparent support of
      7  * gzip'd print files, PPD files, etc.
      8  *
      9  * Copyright 2007-2015 by Apple Inc.
     10  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
     11  *
     12  * These coded instructions, statements, and computer programs are the
     13  * property of Apple Inc. and are protected by Federal copyright
     14  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
     15  * which should have been included with this file.  If this file is
     16  * missing or damaged, see the license at "http://www.cups.org/".
     17  *
     18  * This file is subject to the Apple OS-Developed Software exception.
     19  */
     20 
     21 /*
     22  * Include necessary headers...
     23  */
     24 
     25 #include "file-private.h"
     26 #include <sys/stat.h>
     27 #include <sys/types.h>
     28 
     29 
     30 /*
     31  * Local functions...
     32  */
     33 
     34 #ifdef HAVE_LIBZ
     35 static ssize_t	cups_compress(cups_file_t *fp, const char *buf, size_t bytes);
     36 #endif /* HAVE_LIBZ */
     37 static ssize_t	cups_fill(cups_file_t *fp);
     38 static int	cups_open(const char *filename, int mode);
     39 static ssize_t	cups_read(cups_file_t *fp, char *buf, size_t bytes);
     40 static ssize_t	cups_write(cups_file_t *fp, const char *buf, size_t bytes);
     41 
     42 
     43 #ifndef WIN32
     44 /*
     45  * '_cupsFileCheck()' - Check the permissions of the given filename.
     46  */
     47 
     48 _cups_fc_result_t			/* O - Check result */
     49 _cupsFileCheck(
     50     const char          *filename,	/* I - Filename to check */
     51     _cups_fc_filetype_t filetype,	/* I - Type of file checks? */
     52     int                 dorootchecks,	/* I - Check for root permissions? */
     53     _cups_fc_func_t     cb,		/* I - Callback function */
     54     void                *context)	/* I - Context pointer for callback */
     55 
     56 {
     57   struct stat		fileinfo;	/* File information */
     58   char			message[1024],	/* Message string */
     59 			temp[1024],	/* Parent directory filename */
     60 			*ptr;		/* Pointer into parent directory */
     61   _cups_fc_result_t	result;		/* Check result */
     62 
     63 
     64  /*
     65   * Does the filename contain a relative path ("../")?
     66   */
     67 
     68   if (strstr(filename, "../"))
     69   {
     70    /*
     71     * Yes, fail it!
     72     */
     73 
     74     result = _CUPS_FILE_CHECK_RELATIVE_PATH;
     75     goto finishup;
     76   }
     77 
     78  /*
     79   * Does the program even exist and is it accessible?
     80   */
     81 
     82   if (stat(filename, &fileinfo))
     83   {
     84    /*
     85     * Nope...
     86     */
     87 
     88     result = _CUPS_FILE_CHECK_MISSING;
     89     goto finishup;
     90   }
     91 
     92  /*
     93   * Check the execute bit...
     94   */
     95 
     96   result = _CUPS_FILE_CHECK_OK;
     97 
     98   switch (filetype)
     99   {
    100     case _CUPS_FILE_CHECK_DIRECTORY :
    101         if (!S_ISDIR(fileinfo.st_mode))
    102 	  result = _CUPS_FILE_CHECK_WRONG_TYPE;
    103         break;
    104 
    105     default :
    106         if (!S_ISREG(fileinfo.st_mode))
    107 	  result = _CUPS_FILE_CHECK_WRONG_TYPE;
    108         break;
    109   }
    110 
    111   if (result)
    112     goto finishup;
    113 
    114  /*
    115   * Are we doing root checks?
    116   */
    117 
    118   if (!dorootchecks)
    119   {
    120    /*
    121     * Nope, so anything (else) goes...
    122     */
    123 
    124     goto finishup;
    125   }
    126 
    127  /*
    128   * Verify permission of the file itself:
    129   *
    130   * 1. Must be owned by root
    131   * 2. Must not be writable by group
    132   * 3. Must not be setuid
    133   * 4. Must not be writable by others
    134   */
    135 
    136   if (fileinfo.st_uid ||		/* 1. Must be owned by root */
    137       (fileinfo.st_mode & S_IWGRP)  ||	/* 2. Must not be writable by group */
    138       (fileinfo.st_mode & S_ISUID) ||	/* 3. Must not be setuid */
    139       (fileinfo.st_mode & S_IWOTH))	/* 4. Must not be writable by others */
    140   {
    141     result = _CUPS_FILE_CHECK_PERMISSIONS;
    142     goto finishup;
    143   }
    144 
    145   if (filetype == _CUPS_FILE_CHECK_DIRECTORY ||
    146       filetype == _CUPS_FILE_CHECK_FILE_ONLY)
    147     goto finishup;
    148 
    149  /*
    150   * Now check the containing directory...
    151   */
    152 
    153   strlcpy(temp, filename, sizeof(temp));
    154   if ((ptr = strrchr(temp, '/')) != NULL)
    155   {
    156     if (ptr == temp)
    157       ptr[1] = '\0';
    158     else
    159       *ptr = '\0';
    160   }
    161 
    162   if (stat(temp, &fileinfo))
    163   {
    164    /*
    165     * Doesn't exist?!?
    166     */
    167 
    168     result   = _CUPS_FILE_CHECK_MISSING;
    169     filetype = _CUPS_FILE_CHECK_DIRECTORY;
    170     filename = temp;
    171 
    172     goto finishup;
    173   }
    174 
    175   if (fileinfo.st_uid ||		/* 1. Must be owned by root */
    176       (fileinfo.st_mode & S_IWGRP) ||	/* 2. Must not be writable by group */
    177       (fileinfo.st_mode & S_ISUID) ||	/* 3. Must not be setuid */
    178       (fileinfo.st_mode & S_IWOTH))	/* 4. Must not be writable by others */
    179   {
    180     result   = _CUPS_FILE_CHECK_PERMISSIONS;
    181     filetype = _CUPS_FILE_CHECK_DIRECTORY;
    182     filename = temp;
    183   }
    184 
    185  /*
    186   * Common return point...
    187   */
    188 
    189   finishup:
    190 
    191   if (cb)
    192   {
    193     cups_lang_t *lang = cupsLangDefault();
    194 					/* Localization information */
    195 
    196     switch (result)
    197     {
    198       case _CUPS_FILE_CHECK_OK :
    199 	  if (filetype == _CUPS_FILE_CHECK_DIRECTORY)
    200 	    snprintf(message, sizeof(message),
    201 		     _cupsLangString(lang, _("Directory \"%s\" permissions OK "
    202 					     "(0%o/uid=%d/gid=%d).")),
    203 		     filename, fileinfo.st_mode, (int)fileinfo.st_uid,
    204 		     (int)fileinfo.st_gid);
    205 	  else
    206 	    snprintf(message, sizeof(message),
    207 		     _cupsLangString(lang, _("File \"%s\" permissions OK "
    208 					     "(0%o/uid=%d/gid=%d).")),
    209 		     filename, fileinfo.st_mode, (int)fileinfo.st_uid,
    210 		     (int)fileinfo.st_gid);
    211           break;
    212 
    213       case _CUPS_FILE_CHECK_MISSING :
    214 	  if (filetype == _CUPS_FILE_CHECK_DIRECTORY)
    215 	    snprintf(message, sizeof(message),
    216 		     _cupsLangString(lang, _("Directory \"%s\" not available: "
    217 					     "%s")),
    218 		     filename, strerror(errno));
    219 	  else
    220 	    snprintf(message, sizeof(message),
    221 		     _cupsLangString(lang, _("File \"%s\" not available: %s")),
    222 		     filename, strerror(errno));
    223           break;
    224 
    225       case _CUPS_FILE_CHECK_PERMISSIONS :
    226 	  if (filetype == _CUPS_FILE_CHECK_DIRECTORY)
    227 	    snprintf(message, sizeof(message),
    228 		     _cupsLangString(lang, _("Directory \"%s\" has insecure "
    229 					     "permissions "
    230 					     "(0%o/uid=%d/gid=%d).")),
    231 		     filename, fileinfo.st_mode, (int)fileinfo.st_uid,
    232 		     (int)fileinfo.st_gid);
    233 	  else
    234 	    snprintf(message, sizeof(message),
    235 		     _cupsLangString(lang, _("File \"%s\" has insecure "
    236 		                             "permissions "
    237 					     "(0%o/uid=%d/gid=%d).")),
    238 		     filename, fileinfo.st_mode, (int)fileinfo.st_uid,
    239 		     (int)fileinfo.st_gid);
    240           break;
    241 
    242       case _CUPS_FILE_CHECK_WRONG_TYPE :
    243 	  if (filetype == _CUPS_FILE_CHECK_DIRECTORY)
    244 	    snprintf(message, sizeof(message),
    245 		     _cupsLangString(lang, _("Directory \"%s\" is a file.")),
    246 		     filename);
    247 	  else
    248 	    snprintf(message, sizeof(message),
    249 		     _cupsLangString(lang, _("File \"%s\" is a directory.")),
    250 		     filename);
    251           break;
    252 
    253       case _CUPS_FILE_CHECK_RELATIVE_PATH :
    254 	  if (filetype == _CUPS_FILE_CHECK_DIRECTORY)
    255 	    snprintf(message, sizeof(message),
    256 		     _cupsLangString(lang, _("Directory \"%s\" contains a "
    257 					     "relative path.")), filename);
    258 	  else
    259 	    snprintf(message, sizeof(message),
    260 		     _cupsLangString(lang, _("File \"%s\" contains a relative "
    261 					     "path.")), filename);
    262           break;
    263     }
    264 
    265     (*cb)(context, result, message);
    266   }
    267 
    268   return (result);
    269 }
    270 
    271 
    272 /*
    273  * '_cupsFileCheckFilter()' - Report file check results as CUPS filter messages.
    274  */
    275 
    276 void
    277 _cupsFileCheckFilter(
    278     void              *context,		/* I - Context pointer (unused) */
    279     _cups_fc_result_t result,		/* I - Result code */
    280     const char        *message)		/* I - Message text */
    281 {
    282   const char	*prefix;		/* Messaging prefix */
    283 
    284 
    285   (void)context;
    286 
    287   switch (result)
    288   {
    289     default :
    290     case _CUPS_FILE_CHECK_OK :
    291         prefix = "DEBUG2";
    292 	break;
    293 
    294     case _CUPS_FILE_CHECK_MISSING :
    295     case _CUPS_FILE_CHECK_WRONG_TYPE :
    296         prefix = "ERROR";
    297 	fputs("STATE: +cups-missing-filter-warning\n", stderr);
    298 	break;
    299 
    300     case _CUPS_FILE_CHECK_PERMISSIONS :
    301     case _CUPS_FILE_CHECK_RELATIVE_PATH :
    302         prefix = "ERROR";
    303 	fputs("STATE: +cups-insecure-filter-warning\n", stderr);
    304 	break;
    305   }
    306 
    307   fprintf(stderr, "%s: %s\n", prefix, message);
    308 }
    309 #endif /* !WIN32 */
    310 
    311 
    312 /*
    313  * 'cupsFileClose()' - Close a CUPS file.
    314  *
    315  * @since CUPS 1.2/macOS 10.5@
    316  */
    317 
    318 int					/* O - 0 on success, -1 on error */
    319 cupsFileClose(cups_file_t *fp)		/* I - CUPS file */
    320 {
    321   int	fd;				/* File descriptor */
    322   char	mode;				/* Open mode */
    323   int	status;				/* Return status */
    324   int	is_stdio;			/* Is a stdio file? */
    325 
    326 
    327   DEBUG_printf(("cupsFileClose(fp=%p)", (void *)fp));
    328 
    329  /*
    330   * Range check...
    331   */
    332 
    333   if (!fp)
    334     return (-1);
    335 
    336  /*
    337   * Flush pending write data...
    338   */
    339 
    340   if (fp->mode == 'w')
    341     status = cupsFileFlush(fp);
    342   else
    343     status = 0;
    344 
    345 #ifdef HAVE_LIBZ
    346   if (fp->compressed && status >= 0)
    347   {
    348     if (fp->mode == 'r')
    349     {
    350      /*
    351       * Free decompression data...
    352       */
    353 
    354       inflateEnd(&fp->stream);
    355     }
    356     else
    357     {
    358      /*
    359       * Flush any remaining compressed data...
    360       */
    361 
    362       unsigned char	trailer[8];	/* Trailer CRC and length */
    363       int		done;		/* Done writing... */
    364 
    365 
    366       fp->stream.avail_in = 0;
    367 
    368       for (done = 0;;)
    369       {
    370         if (fp->stream.next_out > fp->cbuf)
    371 	{
    372 	  if (cups_write(fp, (char *)fp->cbuf,
    373 	                 (size_t)(fp->stream.next_out - fp->cbuf)) < 0)
    374 	    status = -1;
    375 
    376 	  fp->stream.next_out  = fp->cbuf;
    377 	  fp->stream.avail_out = sizeof(fp->cbuf);
    378 	}
    379 
    380         if (done || status < 0)
    381 	  break;
    382 
    383         done = deflate(&fp->stream, Z_FINISH) == Z_STREAM_END &&
    384 	       fp->stream.next_out == fp->cbuf;
    385       }
    386 
    387      /*
    388       * Write the CRC and length...
    389       */
    390 
    391       trailer[0] = (unsigned char)fp->crc;
    392       trailer[1] = (unsigned char)(fp->crc >> 8);
    393       trailer[2] = (unsigned char)(fp->crc >> 16);
    394       trailer[3] = (unsigned char)(fp->crc >> 24);
    395       trailer[4] = (unsigned char)fp->pos;
    396       trailer[5] = (unsigned char)(fp->pos >> 8);
    397       trailer[6] = (unsigned char)(fp->pos >> 16);
    398       trailer[7] = (unsigned char)(fp->pos >> 24);
    399 
    400       if (cups_write(fp, (char *)trailer, 8) < 0)
    401         status = -1;
    402 
    403      /*
    404       * Free all memory used by the compression stream...
    405       */
    406 
    407       deflateEnd(&(fp->stream));
    408     }
    409   }
    410 #endif /* HAVE_LIBZ */
    411 
    412  /*
    413   * Save the file descriptor we used and free memory...
    414   */
    415 
    416   fd       = fp->fd;
    417   mode     = fp->mode;
    418   is_stdio = fp->is_stdio;
    419 
    420   if (fp->printf_buffer)
    421     free(fp->printf_buffer);
    422 
    423   free(fp);
    424 
    425  /*
    426   * Close the file, returning the close status...
    427   */
    428 
    429   if (mode == 's')
    430   {
    431     if (httpAddrClose(NULL, fd) < 0)
    432       status = -1;
    433   }
    434   else if (!is_stdio)
    435   {
    436     if (close(fd) < 0)
    437       status = -1;
    438   }
    439 
    440   return (status);
    441 }
    442 
    443 
    444 /*
    445  * 'cupsFileCompression()' - Return whether a file is compressed.
    446  *
    447  * @since CUPS 1.2/macOS 10.5@
    448  */
    449 
    450 int					/* O - @code CUPS_FILE_NONE@ or @code CUPS_FILE_GZIP@ */
    451 cupsFileCompression(cups_file_t *fp)	/* I - CUPS file */
    452 {
    453   return (fp ? fp->compressed : CUPS_FILE_NONE);
    454 }
    455 
    456 
    457 /*
    458  * 'cupsFileEOF()' - Return the end-of-file status.
    459  *
    460  * @since CUPS 1.2/macOS 10.5@
    461  */
    462 
    463 int					/* O - 1 on end of file, 0 otherwise */
    464 cupsFileEOF(cups_file_t *fp)		/* I - CUPS file */
    465 {
    466   return (fp ? fp->eof : 1);
    467 }
    468 
    469 
    470 /*
    471  * 'cupsFileFind()' - Find a file using the specified path.
    472  *
    473  * This function allows the paths in the path string to be separated by
    474  * colons (UNIX standard) or semicolons (Windows standard) and stores the
    475  * result in the buffer supplied.  If the file cannot be found in any of
    476  * the supplied paths, @code NULL@ is returned. A @code NULL@ path only
    477  * matches the current directory.
    478  *
    479  * @since CUPS 1.2/macOS 10.5@
    480  */
    481 
    482 const char *				/* O - Full path to file or @code NULL@ if not found */
    483 cupsFileFind(const char *filename,	/* I - File to find */
    484              const char *path,		/* I - Colon/semicolon-separated path */
    485              int        executable,	/* I - 1 = executable files, 0 = any file/dir */
    486 	     char       *buffer,	/* I - Filename buffer */
    487 	     int        bufsize)	/* I - Size of filename buffer */
    488 {
    489   char	*bufptr,			/* Current position in buffer */
    490 	*bufend;			/* End of buffer */
    491 
    492 
    493  /*
    494   * Range check input...
    495   */
    496 
    497   DEBUG_printf(("cupsFileFind(filename=\"%s\", path=\"%s\", executable=%d, buffer=%p, bufsize=%d)", filename, path, executable, (void *)buffer, bufsize));
    498 
    499   if (!filename || !buffer || bufsize < 2)
    500     return (NULL);
    501 
    502   if (!path)
    503   {
    504    /*
    505     * No path, so check current directory...
    506     */
    507 
    508     if (!access(filename, 0))
    509     {
    510       strlcpy(buffer, filename, (size_t)bufsize);
    511       return (buffer);
    512     }
    513     else
    514       return (NULL);
    515   }
    516 
    517  /*
    518   * Now check each path and return the first match...
    519   */
    520 
    521   bufend = buffer + bufsize - 1;
    522   bufptr = buffer;
    523 
    524   while (*path)
    525   {
    526 #ifdef WIN32
    527     if (*path == ';' || (*path == ':' && ((bufptr - buffer) > 1 || !isalpha(buffer[0] & 255))))
    528 #else
    529     if (*path == ';' || *path == ':')
    530 #endif /* WIN32 */
    531     {
    532       if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend)
    533         *bufptr++ = '/';
    534 
    535       strlcpy(bufptr, filename, (size_t)(bufend - bufptr));
    536 
    537 #ifdef WIN32
    538       if (!access(buffer, 0))
    539 #else
    540       if (!access(buffer, executable ? X_OK : 0))
    541 #endif /* WIN32 */
    542       {
    543         DEBUG_printf(("1cupsFileFind: Returning \"%s\"", buffer));
    544         return (buffer);
    545       }
    546 
    547       bufptr = buffer;
    548     }
    549     else if (bufptr < bufend)
    550       *bufptr++ = *path;
    551 
    552     path ++;
    553   }
    554 
    555  /*
    556   * Check the last path...
    557   */
    558 
    559   if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend)
    560     *bufptr++ = '/';
    561 
    562   strlcpy(bufptr, filename, (size_t)(bufend - bufptr));
    563 
    564   if (!access(buffer, 0))
    565   {
    566     DEBUG_printf(("1cupsFileFind: Returning \"%s\"", buffer));
    567     return (buffer);
    568   }
    569   else
    570   {
    571     DEBUG_puts("1cupsFileFind: Returning NULL");
    572     return (NULL);
    573   }
    574 }
    575 
    576 
    577 /*
    578  * 'cupsFileFlush()' - Flush pending output.
    579  *
    580  * @since CUPS 1.2/macOS 10.5@
    581  */
    582 
    583 int					/* O - 0 on success, -1 on error */
    584 cupsFileFlush(cups_file_t *fp)		/* I - CUPS file */
    585 {
    586   ssize_t	bytes;			/* Bytes to write */
    587 
    588 
    589   DEBUG_printf(("cupsFileFlush(fp=%p)", (void *)fp));
    590 
    591  /*
    592   * Range check input...
    593   */
    594 
    595   if (!fp || fp->mode != 'w')
    596   {
    597     DEBUG_puts("1cupsFileFlush: Attempt to flush a read-only file...");
    598     return (-1);
    599   }
    600 
    601   bytes = (ssize_t)(fp->ptr - fp->buf);
    602 
    603   DEBUG_printf(("2cupsFileFlush: Flushing " CUPS_LLFMT " bytes...",
    604                 CUPS_LLCAST bytes));
    605 
    606   if (bytes > 0)
    607   {
    608 #ifdef HAVE_LIBZ
    609     if (fp->compressed)
    610       bytes = cups_compress(fp, fp->buf, (size_t)bytes);
    611     else
    612 #endif /* HAVE_LIBZ */
    613       bytes = cups_write(fp, fp->buf, (size_t)bytes);
    614 
    615     if (bytes < 0)
    616       return (-1);
    617 
    618     fp->ptr = fp->buf;
    619   }
    620 
    621   return (0);
    622 }
    623 
    624 
    625 /*
    626  * 'cupsFileGetChar()' - Get a single character from a file.
    627  *
    628  * @since CUPS 1.2/macOS 10.5@
    629  */
    630 
    631 int					/* O - Character or -1 on end of file */
    632 cupsFileGetChar(cups_file_t *fp)	/* I - CUPS file */
    633 {
    634  /*
    635   * Range check input...
    636   */
    637 
    638   DEBUG_printf(("4cupsFileGetChar(fp=%p)", (void *)fp));
    639 
    640   if (!fp || (fp->mode != 'r' && fp->mode != 's'))
    641   {
    642     DEBUG_puts("5cupsFileGetChar: Bad arguments!");
    643     return (-1);
    644   }
    645 
    646  /*
    647   * If the input buffer is empty, try to read more data...
    648   */
    649 
    650   DEBUG_printf(("5cupsFileGetChar: fp->eof=%d, fp->ptr=%p, fp->end=%p", fp->eof, (void *)fp->ptr, (void *)fp->end));
    651 
    652   if (fp->ptr >= fp->end)
    653     if (cups_fill(fp) <= 0)
    654     {
    655       DEBUG_puts("5cupsFileGetChar: Unable to fill buffer!");
    656       return (-1);
    657     }
    658 
    659  /*
    660   * Return the next character in the buffer...
    661   */
    662 
    663   DEBUG_printf(("5cupsFileGetChar: Returning %d...", *(fp->ptr) & 255));
    664 
    665   fp->pos ++;
    666 
    667   DEBUG_printf(("6cupsFileGetChar: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
    668 
    669   return (*(fp->ptr)++ & 255);
    670 }
    671 
    672 
    673 /*
    674  * 'cupsFileGetConf()' - Get a line from a configuration file.
    675  *
    676  * @since CUPS 1.2/macOS 10.5@
    677  */
    678 
    679 char *					/* O  - Line read or @code NULL@ on end of file or error */
    680 cupsFileGetConf(cups_file_t *fp,	/* I  - CUPS file */
    681                 char        *buf,	/* O  - String buffer */
    682 		size_t      buflen,	/* I  - Size of string buffer */
    683                 char        **value,	/* O  - Pointer to value */
    684 		int         *linenum)	/* IO - Current line number */
    685 {
    686   char	*ptr;				/* Pointer into line */
    687 
    688 
    689  /*
    690   * Range check input...
    691   */
    692 
    693   DEBUG_printf(("2cupsFileGetConf(fp=%p, buf=%p, buflen=" CUPS_LLFMT
    694                 ", value=%p, linenum=%p)", (void *)fp, (void *)buf, CUPS_LLCAST buflen, (void *)value, (void *)linenum));
    695 
    696   if (!fp || (fp->mode != 'r' && fp->mode != 's') ||
    697       !buf || buflen < 2 || !value)
    698   {
    699     if (value)
    700       *value = NULL;
    701 
    702     return (NULL);
    703   }
    704 
    705  /*
    706   * Read the next non-comment line...
    707   */
    708 
    709   *value = NULL;
    710 
    711   while (cupsFileGets(fp, buf, buflen))
    712   {
    713     (*linenum) ++;
    714 
    715    /*
    716     * Strip any comments...
    717     */
    718 
    719     if ((ptr = strchr(buf, '#')) != NULL)
    720     {
    721       if (ptr > buf && ptr[-1] == '\\')
    722       {
    723         // Unquote the #...
    724 	_cups_strcpy(ptr - 1, ptr);
    725       }
    726       else
    727       {
    728         // Strip the comment and any trailing whitespace...
    729 	while (ptr > buf)
    730 	{
    731 	  if (!_cups_isspace(ptr[-1]))
    732 	    break;
    733 
    734 	  ptr --;
    735 	}
    736 
    737 	*ptr = '\0';
    738       }
    739     }
    740 
    741    /*
    742     * Strip leading whitespace...
    743     */
    744 
    745     for (ptr = buf; _cups_isspace(*ptr); ptr ++);
    746 
    747     if (ptr > buf)
    748       _cups_strcpy(buf, ptr);
    749 
    750    /*
    751     * See if there is anything left...
    752     */
    753 
    754     if (buf[0])
    755     {
    756      /*
    757       * Yes, grab any value and return...
    758       */
    759 
    760       for (ptr = buf; *ptr; ptr ++)
    761         if (_cups_isspace(*ptr))
    762 	  break;
    763 
    764       if (*ptr)
    765       {
    766        /*
    767         * Have a value, skip any other spaces...
    768 	*/
    769 
    770         while (_cups_isspace(*ptr))
    771 	  *ptr++ = '\0';
    772 
    773         if (*ptr)
    774 	  *value = ptr;
    775 
    776        /*
    777         * Strip trailing whitespace and > for lines that begin with <...
    778 	*/
    779 
    780         ptr += strlen(ptr) - 1;
    781 
    782         if (buf[0] == '<' && *ptr == '>')
    783 	  *ptr-- = '\0';
    784 	else if (buf[0] == '<' && *ptr != '>')
    785         {
    786 	 /*
    787 	  * Syntax error...
    788 	  */
    789 
    790 	  *value = NULL;
    791 	  return (buf);
    792 	}
    793 
    794         while (ptr > *value && _cups_isspace(*ptr))
    795 	  *ptr-- = '\0';
    796       }
    797 
    798      /*
    799       * Return the line...
    800       */
    801 
    802       return (buf);
    803     }
    804   }
    805 
    806   return (NULL);
    807 }
    808 
    809 
    810 /*
    811  * 'cupsFileGetLine()' - Get a CR and/or LF-terminated line that may
    812  *                       contain binary data.
    813  *
    814  * This function differs from @link cupsFileGets@ in that the trailing CR
    815  * and LF are preserved, as is any binary data on the line. The buffer is
    816  * nul-terminated, however you should use the returned length to determine
    817  * the number of bytes on the line.
    818  *
    819  * @since CUPS 1.2/macOS 10.5@
    820  */
    821 
    822 size_t					/* O - Number of bytes on line or 0 on end of file */
    823 cupsFileGetLine(cups_file_t *fp,	/* I - File to read from */
    824                 char        *buf,	/* I - Buffer */
    825                 size_t      buflen)	/* I - Size of buffer */
    826 {
    827   int		ch;			/* Character from file */
    828   char		*ptr,			/* Current position in line buffer */
    829 		*end;			/* End of line buffer */
    830 
    831 
    832  /*
    833   * Range check input...
    834   */
    835 
    836   DEBUG_printf(("2cupsFileGetLine(fp=%p, buf=%p, buflen=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST buflen));
    837 
    838   if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 3)
    839     return (0);
    840 
    841  /*
    842   * Now loop until we have a valid line...
    843   */
    844 
    845   for (ptr = buf, end = buf + buflen - 2; ptr < end ;)
    846   {
    847     if (fp->ptr >= fp->end)
    848       if (cups_fill(fp) <= 0)
    849         break;
    850 
    851     *ptr++ = ch = *(fp->ptr)++;
    852     fp->pos ++;
    853 
    854     if (ch == '\r')
    855     {
    856      /*
    857       * Check for CR LF...
    858       */
    859 
    860       if (fp->ptr >= fp->end)
    861 	if (cups_fill(fp) <= 0)
    862           break;
    863 
    864       if (*(fp->ptr) == '\n')
    865       {
    866         *ptr++ = *(fp->ptr)++;
    867 	fp->pos ++;
    868       }
    869 
    870       break;
    871     }
    872     else if (ch == '\n')
    873     {
    874      /*
    875       * Line feed ends a line...
    876       */
    877 
    878       break;
    879     }
    880   }
    881 
    882   *ptr = '\0';
    883 
    884   DEBUG_printf(("4cupsFileGetLine: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
    885 
    886   return ((size_t)(ptr - buf));
    887 }
    888 
    889 
    890 /*
    891  * 'cupsFileGets()' - Get a CR and/or LF-terminated line.
    892  *
    893  * @since CUPS 1.2/macOS 10.5@
    894  */
    895 
    896 char *					/* O - Line read or @code NULL@ on end of file or error */
    897 cupsFileGets(cups_file_t *fp,		/* I - CUPS file */
    898              char        *buf,		/* O - String buffer */
    899 	     size_t      buflen)	/* I - Size of string buffer */
    900 {
    901   int		ch;			/* Character from file */
    902   char		*ptr,			/* Current position in line buffer */
    903 		*end;			/* End of line buffer */
    904 
    905 
    906  /*
    907   * Range check input...
    908   */
    909 
    910   DEBUG_printf(("2cupsFileGets(fp=%p, buf=%p, buflen=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST buflen));
    911 
    912   if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 2)
    913     return (NULL);
    914 
    915  /*
    916   * Now loop until we have a valid line...
    917   */
    918 
    919   for (ptr = buf, end = buf + buflen - 1; ptr < end ;)
    920   {
    921     if (fp->ptr >= fp->end)
    922       if (cups_fill(fp) <= 0)
    923       {
    924         if (ptr == buf)
    925 	  return (NULL);
    926 	else
    927           break;
    928       }
    929 
    930     ch = *(fp->ptr)++;
    931     fp->pos ++;
    932 
    933     if (ch == '\r')
    934     {
    935      /*
    936       * Check for CR LF...
    937       */
    938 
    939       if (fp->ptr >= fp->end)
    940 	if (cups_fill(fp) <= 0)
    941           break;
    942 
    943       if (*(fp->ptr) == '\n')
    944       {
    945         fp->ptr ++;
    946 	fp->pos ++;
    947       }
    948 
    949       break;
    950     }
    951     else if (ch == '\n')
    952     {
    953      /*
    954       * Line feed ends a line...
    955       */
    956 
    957       break;
    958     }
    959     else
    960       *ptr++ = (char)ch;
    961   }
    962 
    963   *ptr = '\0';
    964 
    965   DEBUG_printf(("4cupsFileGets: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
    966 
    967   return (buf);
    968 }
    969 
    970 
    971 /*
    972  * 'cupsFileLock()' - Temporarily lock access to a file.
    973  *
    974  * @since CUPS 1.2/macOS 10.5@
    975  */
    976 
    977 int					/* O - 0 on success, -1 on error */
    978 cupsFileLock(cups_file_t *fp,		/* I - CUPS file */
    979              int         block)		/* I - 1 to wait for the lock, 0 to fail right away */
    980 {
    981  /*
    982   * Range check...
    983   */
    984 
    985   if (!fp || fp->mode == 's')
    986     return (-1);
    987 
    988  /*
    989   * Try the lock...
    990   */
    991 
    992 #ifdef WIN32
    993   return (_locking(fp->fd, block ? _LK_LOCK : _LK_NBLCK, 0));
    994 #else
    995   return (lockf(fp->fd, block ? F_LOCK : F_TLOCK, 0));
    996 #endif /* WIN32 */
    997 }
    998 
    999 
   1000 /*
   1001  * 'cupsFileNumber()' - Return the file descriptor associated with a CUPS file.
   1002  *
   1003  * @since CUPS 1.2/macOS 10.5@
   1004  */
   1005 
   1006 int					/* O - File descriptor */
   1007 cupsFileNumber(cups_file_t *fp)		/* I - CUPS file */
   1008 {
   1009   if (fp)
   1010     return (fp->fd);
   1011   else
   1012     return (-1);
   1013 }
   1014 
   1015 
   1016 /*
   1017  * 'cupsFileOpen()' - Open a CUPS file.
   1018  *
   1019  * The "mode" parameter can be "r" to read, "w" to write, overwriting any
   1020  * existing file, "a" to append to an existing file or create a new file,
   1021  * or "s" to open a socket connection.
   1022  *
   1023  * When opening for writing ("w"), an optional number from 1 to 9 can be
   1024  * supplied which enables Flate compression of the file.  Compression is
   1025  * not supported for the "a" (append) mode.
   1026  *
   1027  * When opening a socket connection, the filename is a string of the form
   1028  * "address:port" or "hostname:port". The socket will make an IPv4 or IPv6
   1029  * connection as needed, generally preferring IPv6 connections when there is
   1030  * a choice.
   1031  *
   1032  * @since CUPS 1.2/macOS 10.5@
   1033  */
   1034 
   1035 cups_file_t *				/* O - CUPS file or @code NULL@ if the file or socket cannot be opened */
   1036 cupsFileOpen(const char *filename,	/* I - Name of file */
   1037              const char *mode)		/* I - Open mode */
   1038 {
   1039   cups_file_t	*fp;			/* New CUPS file */
   1040   int		fd;			/* File descriptor */
   1041   char		hostname[1024],		/* Hostname */
   1042 		*portname;		/* Port "name" (number or service) */
   1043   http_addrlist_t *addrlist;		/* Host address list */
   1044 
   1045 
   1046   DEBUG_printf(("cupsFileOpen(filename=\"%s\", mode=\"%s\")", filename,
   1047                 mode));
   1048 
   1049  /*
   1050   * Range check input...
   1051   */
   1052 
   1053   if (!filename || !mode ||
   1054       (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') ||
   1055       (*mode == 'a' && isdigit(mode[1] & 255)))
   1056     return (NULL);
   1057 
   1058  /*
   1059   * Open the file...
   1060   */
   1061 
   1062   switch (*mode)
   1063   {
   1064     case 'a' : /* Append file */
   1065         fd = cups_open(filename,
   1066 		       O_RDWR | O_CREAT | O_APPEND | O_LARGEFILE | O_BINARY);
   1067         break;
   1068 
   1069     case 'r' : /* Read file */
   1070 	fd = open(filename, O_RDONLY | O_LARGEFILE | O_BINARY, 0);
   1071 	break;
   1072 
   1073     case 'w' : /* Write file */
   1074         fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY);
   1075 	if (fd < 0 && errno == ENOENT)
   1076 	{
   1077 	  fd = cups_open(filename,
   1078 	                 O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE | O_BINARY);
   1079 	  if (fd < 0 && errno == EEXIST)
   1080 	    fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY);
   1081 	}
   1082 
   1083 	if (fd >= 0)
   1084 #ifdef WIN32
   1085 	  _chsize(fd, 0);
   1086 #else
   1087 	  ftruncate(fd, 0);
   1088 #endif /* WIN32 */
   1089         break;
   1090 
   1091     case 's' : /* Read/write socket */
   1092         strlcpy(hostname, filename, sizeof(hostname));
   1093 	if ((portname = strrchr(hostname, ':')) != NULL)
   1094 	  *portname++ = '\0';
   1095 	else
   1096 	  return (NULL);
   1097 
   1098        /*
   1099         * Lookup the hostname and service...
   1100 	*/
   1101 
   1102         if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
   1103 	  return (NULL);
   1104 
   1105        /*
   1106 	* Connect to the server...
   1107 	*/
   1108 
   1109         if (!httpAddrConnect(addrlist, &fd))
   1110 	{
   1111 	  httpAddrFreeList(addrlist);
   1112 	  return (NULL);
   1113 	}
   1114 
   1115 	httpAddrFreeList(addrlist);
   1116 	break;
   1117 
   1118     default : /* Remove bogus compiler warning... */
   1119         return (NULL);
   1120   }
   1121 
   1122   if (fd < 0)
   1123     return (NULL);
   1124 
   1125  /*
   1126   * Create the CUPS file structure...
   1127   */
   1128 
   1129   if ((fp = cupsFileOpenFd(fd, mode)) == NULL)
   1130   {
   1131     if (*mode == 's')
   1132       httpAddrClose(NULL, fd);
   1133     else
   1134       close(fd);
   1135   }
   1136 
   1137  /*
   1138   * Return it...
   1139   */
   1140 
   1141   return (fp);
   1142 }
   1143 
   1144 /*
   1145  * 'cupsFileOpenFd()' - Open a CUPS file using a file descriptor.
   1146  *
   1147  * The "mode" parameter can be "r" to read, "w" to write, "a" to append,
   1148  * or "s" to treat the file descriptor as a bidirectional socket connection.
   1149  *
   1150  * When opening for writing ("w"), an optional number from 1 to 9 can be
   1151  * supplied which enables Flate compression of the file.  Compression is
   1152  * not supported for the "a" (append) mode.
   1153  *
   1154  * @since CUPS 1.2/macOS 10.5@
   1155  */
   1156 
   1157 cups_file_t *				/* O - CUPS file or @code NULL@ if the file could not be opened */
   1158 cupsFileOpenFd(int        fd,		/* I - File descriptor */
   1159 	       const char *mode)	/* I - Open mode */
   1160 {
   1161   cups_file_t	*fp;			/* New CUPS file */
   1162 
   1163 
   1164   DEBUG_printf(("cupsFileOpenFd(fd=%d, mode=\"%s\")", fd, mode));
   1165 
   1166  /*
   1167   * Range check input...
   1168   */
   1169 
   1170   if (fd < 0 || !mode ||
   1171       (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') ||
   1172       (*mode == 'a' && isdigit(mode[1] & 255)))
   1173     return (NULL);
   1174 
   1175  /*
   1176   * Allocate memory...
   1177   */
   1178 
   1179   if ((fp = calloc(1, sizeof(cups_file_t))) == NULL)
   1180     return (NULL);
   1181 
   1182  /*
   1183   * Open the file...
   1184   */
   1185 
   1186   fp->fd = fd;
   1187 
   1188   switch (*mode)
   1189   {
   1190     case 'a' :
   1191         fp->pos = lseek(fd, 0, SEEK_END);
   1192 
   1193     case 'w' :
   1194 	fp->mode = 'w';
   1195 	fp->ptr  = fp->buf;
   1196 	fp->end  = fp->buf + sizeof(fp->buf);
   1197 
   1198 #ifdef HAVE_LIBZ
   1199 	if (mode[1] >= '1' && mode[1] <= '9')
   1200 	{
   1201 	 /*
   1202 	  * Open a compressed stream, so write the standard gzip file
   1203 	  * header...
   1204 	  */
   1205 
   1206           unsigned char header[10];	/* gzip file header */
   1207 	  time_t	curtime;	/* Current time */
   1208 
   1209 
   1210           curtime   = time(NULL);
   1211 	  header[0] = 0x1f;
   1212 	  header[1] = 0x8b;
   1213 	  header[2] = Z_DEFLATED;
   1214 	  header[3] = 0;
   1215 	  header[4] = (unsigned char)curtime;
   1216 	  header[5] = (unsigned char)(curtime >> 8);
   1217 	  header[6] = (unsigned char)(curtime >> 16);
   1218 	  header[7] = (unsigned char)(curtime >> 24);
   1219 	  header[8] = 0;
   1220 	  header[9] = 0x03;
   1221 
   1222 	  cups_write(fp, (char *)header, 10);
   1223 
   1224          /*
   1225 	  * Initialize the compressor...
   1226 	  */
   1227 
   1228           deflateInit2(&(fp->stream), mode[1] - '0', Z_DEFLATED, -15, 8,
   1229 	               Z_DEFAULT_STRATEGY);
   1230 
   1231 	  fp->stream.next_out  = fp->cbuf;
   1232 	  fp->stream.avail_out = sizeof(fp->cbuf);
   1233 	  fp->compressed       = 1;
   1234 	  fp->crc              = crc32(0L, Z_NULL, 0);
   1235 	}
   1236 #endif /* HAVE_LIBZ */
   1237         break;
   1238 
   1239     case 'r' :
   1240 	fp->mode = 'r';
   1241 	break;
   1242 
   1243     case 's' :
   1244         fp->mode = 's';
   1245 	break;
   1246 
   1247     default : /* Remove bogus compiler warning... */
   1248         return (NULL);
   1249   }
   1250 
   1251  /*
   1252   * Don't pass this file to child processes...
   1253   */
   1254 
   1255 #ifndef WIN32
   1256   fcntl(fp->fd, F_SETFD, fcntl(fp->fd, F_GETFD) | FD_CLOEXEC);
   1257 #endif /* !WIN32 */
   1258 
   1259   return (fp);
   1260 }
   1261 
   1262 
   1263 /*
   1264  * 'cupsFilePeekChar()' - Peek at the next character from a file.
   1265  *
   1266  * @since CUPS 1.2/macOS 10.5@
   1267  */
   1268 
   1269 int					/* O - Character or -1 on end of file */
   1270 cupsFilePeekChar(cups_file_t *fp)	/* I - CUPS file */
   1271 {
   1272  /*
   1273   * Range check input...
   1274   */
   1275 
   1276   if (!fp || (fp->mode != 'r' && fp->mode != 's'))
   1277     return (-1);
   1278 
   1279  /*
   1280   * If the input buffer is empty, try to read more data...
   1281   */
   1282 
   1283   if (fp->ptr >= fp->end)
   1284     if (cups_fill(fp) <= 0)
   1285       return (-1);
   1286 
   1287  /*
   1288   * Return the next character in the buffer...
   1289   */
   1290 
   1291   return (*(fp->ptr) & 255);
   1292 }
   1293 
   1294 
   1295 /*
   1296  * 'cupsFilePrintf()' - Write a formatted string.
   1297  *
   1298  * @since CUPS 1.2/macOS 10.5@
   1299  */
   1300 
   1301 int					/* O - Number of bytes written or -1 on error */
   1302 cupsFilePrintf(cups_file_t *fp,		/* I - CUPS file */
   1303                const char  *format,	/* I - Printf-style format string */
   1304 	       ...)			/* I - Additional args as necessary */
   1305 {
   1306   va_list	ap;			/* Argument list */
   1307   ssize_t	bytes;			/* Formatted size */
   1308 
   1309 
   1310   DEBUG_printf(("2cupsFilePrintf(fp=%p, format=\"%s\", ...)", (void *)fp, format));
   1311 
   1312   if (!fp || !format || (fp->mode != 'w' && fp->mode != 's'))
   1313     return (-1);
   1314 
   1315   if (!fp->printf_buffer)
   1316   {
   1317    /*
   1318     * Start with an 1k printf buffer...
   1319     */
   1320 
   1321     if ((fp->printf_buffer = malloc(1024)) == NULL)
   1322       return (-1);
   1323 
   1324     fp->printf_size = 1024;
   1325   }
   1326 
   1327   va_start(ap, format);
   1328   bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap);
   1329   va_end(ap);
   1330 
   1331   if (bytes >= (ssize_t)fp->printf_size)
   1332   {
   1333    /*
   1334     * Expand the printf buffer...
   1335     */
   1336 
   1337     char	*temp;			/* Temporary buffer pointer */
   1338 
   1339 
   1340     if (bytes > 65535)
   1341       return (-1);
   1342 
   1343     if ((temp = realloc(fp->printf_buffer, (size_t)(bytes + 1))) == NULL)
   1344       return (-1);
   1345 
   1346     fp->printf_buffer = temp;
   1347     fp->printf_size   = (size_t)(bytes + 1);
   1348 
   1349     va_start(ap, format);
   1350     bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap);
   1351     va_end(ap);
   1352   }
   1353 
   1354   if (fp->mode == 's')
   1355   {
   1356     if (cups_write(fp, fp->printf_buffer, (size_t)bytes) < 0)
   1357       return (-1);
   1358 
   1359     fp->pos += bytes;
   1360 
   1361     DEBUG_printf(("4cupsFilePrintf: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
   1362 
   1363     return ((int)bytes);
   1364   }
   1365 
   1366   if ((fp->ptr + bytes) > fp->end)
   1367     if (cupsFileFlush(fp))
   1368       return (-1);
   1369 
   1370   fp->pos += bytes;
   1371 
   1372   DEBUG_printf(("4cupsFilePrintf: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
   1373 
   1374   if ((size_t)bytes > sizeof(fp->buf))
   1375   {
   1376 #ifdef HAVE_LIBZ
   1377     if (fp->compressed)
   1378       return ((int)cups_compress(fp, fp->printf_buffer, (size_t)bytes));
   1379     else
   1380 #endif /* HAVE_LIBZ */
   1381       return ((int)cups_write(fp, fp->printf_buffer, (size_t)bytes));
   1382   }
   1383   else
   1384   {
   1385     memcpy(fp->ptr, fp->printf_buffer, (size_t)bytes);
   1386     fp->ptr += bytes;
   1387     return ((int)bytes);
   1388   }
   1389 }
   1390 
   1391 
   1392 /*
   1393  * 'cupsFilePutChar()' - Write a character.
   1394  *
   1395  * @since CUPS 1.2/macOS 10.5@
   1396  */
   1397 
   1398 int					/* O - 0 on success, -1 on error */
   1399 cupsFilePutChar(cups_file_t *fp,	/* I - CUPS file */
   1400                 int         c)		/* I - Character to write */
   1401 {
   1402  /*
   1403   * Range check input...
   1404   */
   1405 
   1406   if (!fp || (fp->mode != 'w' && fp->mode != 's'))
   1407     return (-1);
   1408 
   1409   if (fp->mode == 's')
   1410   {
   1411    /*
   1412     * Send character immediately over socket...
   1413     */
   1414 
   1415     char ch;				/* Output character */
   1416 
   1417 
   1418     ch = (char)c;
   1419 
   1420     if (send(fp->fd, &ch, 1, 0) < 1)
   1421       return (-1);
   1422   }
   1423   else
   1424   {
   1425    /*
   1426     * Buffer it up...
   1427     */
   1428 
   1429     if (fp->ptr >= fp->end)
   1430       if (cupsFileFlush(fp))
   1431 	return (-1);
   1432 
   1433     *(fp->ptr) ++ = (char)c;
   1434   }
   1435 
   1436   fp->pos ++;
   1437 
   1438   DEBUG_printf(("4cupsFilePutChar: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
   1439 
   1440   return (0);
   1441 }
   1442 
   1443 
   1444 /*
   1445  * 'cupsFilePutConf()' - Write a configuration line.
   1446  *
   1447  * This function handles any comment escaping of the value.
   1448  *
   1449  * @since CUPS 1.4/macOS 10.6@
   1450  */
   1451 
   1452 ssize_t					/* O - Number of bytes written or -1 on error */
   1453 cupsFilePutConf(cups_file_t *fp,	/* I - CUPS file */
   1454                 const char *directive,	/* I - Directive */
   1455 		const char *value)	/* I - Value */
   1456 {
   1457   ssize_t	bytes,			/* Number of bytes written */
   1458 		temp;			/* Temporary byte count */
   1459   const char	*ptr;			/* Pointer into value */
   1460 
   1461 
   1462   if (!fp || !directive || !*directive)
   1463     return (-1);
   1464 
   1465   if ((bytes = cupsFilePuts(fp, directive)) < 0)
   1466     return (-1);
   1467 
   1468   if (cupsFilePutChar(fp, ' ') < 0)
   1469     return (-1);
   1470   bytes ++;
   1471 
   1472   if (value && *value)
   1473   {
   1474     if ((ptr = strchr(value, '#')) != NULL)
   1475     {
   1476      /*
   1477       * Need to quote the first # in the info string...
   1478       */
   1479 
   1480       if ((temp = cupsFileWrite(fp, value, (size_t)(ptr - value))) < 0)
   1481         return (-1);
   1482       bytes += temp;
   1483 
   1484       if (cupsFilePutChar(fp, '\\') < 0)
   1485         return (-1);
   1486       bytes ++;
   1487 
   1488       if ((temp = cupsFilePuts(fp, ptr)) < 0)
   1489         return (-1);
   1490       bytes += temp;
   1491     }
   1492     else if ((temp = cupsFilePuts(fp, value)) < 0)
   1493       return (-1);
   1494     else
   1495       bytes += temp;
   1496   }
   1497 
   1498   if (cupsFilePutChar(fp, '\n') < 0)
   1499     return (-1);
   1500   else
   1501     return (bytes + 1);
   1502 }
   1503 
   1504 
   1505 /*
   1506  * 'cupsFilePuts()' - Write a string.
   1507  *
   1508  * Like the @code fputs@ function, no newline is appended to the string.
   1509  *
   1510  * @since CUPS 1.2/macOS 10.5@
   1511  */
   1512 
   1513 int					/* O - Number of bytes written or -1 on error */
   1514 cupsFilePuts(cups_file_t *fp,		/* I - CUPS file */
   1515              const char  *s)		/* I - String to write */
   1516 {
   1517   ssize_t	bytes;			/* Bytes to write */
   1518 
   1519 
   1520  /*
   1521   * Range check input...
   1522   */
   1523 
   1524   if (!fp || !s || (fp->mode != 'w' && fp->mode != 's'))
   1525     return (-1);
   1526 
   1527  /*
   1528   * Write the string...
   1529   */
   1530 
   1531   bytes = (ssize_t)strlen(s);
   1532 
   1533   if (fp->mode == 's')
   1534   {
   1535     if (cups_write(fp, s, (size_t)bytes) < 0)
   1536       return (-1);
   1537 
   1538     fp->pos += bytes;
   1539 
   1540     DEBUG_printf(("4cupsFilePuts: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
   1541 
   1542     return ((int)bytes);
   1543   }
   1544 
   1545   if ((fp->ptr + bytes) > fp->end)
   1546     if (cupsFileFlush(fp))
   1547       return (-1);
   1548 
   1549   fp->pos += bytes;
   1550 
   1551   DEBUG_printf(("4cupsFilePuts: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
   1552 
   1553   if ((size_t)bytes > sizeof(fp->buf))
   1554   {
   1555 #ifdef HAVE_LIBZ
   1556     if (fp->compressed)
   1557       return ((int)cups_compress(fp, s, (size_t)bytes));
   1558     else
   1559 #endif /* HAVE_LIBZ */
   1560       return ((int)cups_write(fp, s, (size_t)bytes));
   1561   }
   1562   else
   1563   {
   1564     memcpy(fp->ptr, s, (size_t)bytes);
   1565     fp->ptr += bytes;
   1566     return ((int)bytes);
   1567   }
   1568 }
   1569 
   1570 
   1571 /*
   1572  * 'cupsFileRead()' - Read from a file.
   1573  *
   1574  * @since CUPS 1.2/macOS 10.5@
   1575  */
   1576 
   1577 ssize_t					/* O - Number of bytes read or -1 on error */
   1578 cupsFileRead(cups_file_t *fp,		/* I - CUPS file */
   1579              char        *buf,		/* O - Buffer */
   1580 	     size_t      bytes)		/* I - Number of bytes to read */
   1581 {
   1582   size_t	total;			/* Total bytes read */
   1583   ssize_t	count;			/* Bytes read */
   1584 
   1585 
   1586   DEBUG_printf(("2cupsFileRead(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes));
   1587 
   1588  /*
   1589   * Range check input...
   1590   */
   1591 
   1592   if (!fp || !buf || (fp->mode != 'r' && fp->mode != 's'))
   1593     return (-1);
   1594 
   1595   if (bytes == 0)
   1596     return (0);
   1597 
   1598  /*
   1599   * Loop until all bytes are read...
   1600   */
   1601 
   1602   total = 0;
   1603   while (bytes > 0)
   1604   {
   1605     if (fp->ptr >= fp->end)
   1606       if (cups_fill(fp) <= 0)
   1607       {
   1608         DEBUG_printf(("4cupsFileRead: cups_fill() returned -1, total="
   1609 	              CUPS_LLFMT, CUPS_LLCAST total));
   1610 
   1611         if (total > 0)
   1612           return ((ssize_t)total);
   1613 	else
   1614 	  return (-1);
   1615       }
   1616 
   1617     count = (ssize_t)(fp->end - fp->ptr);
   1618     if (count > (ssize_t)bytes)
   1619       count = (ssize_t)bytes;
   1620 
   1621     memcpy(buf, fp->ptr,(size_t) count);
   1622     fp->ptr += count;
   1623     fp->pos += count;
   1624 
   1625     DEBUG_printf(("4cupsFileRead: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
   1626 
   1627    /*
   1628     * Update the counts for the last read...
   1629     */
   1630 
   1631     bytes -= (size_t)count;
   1632     total += (size_t)count;
   1633     buf   += count;
   1634   }
   1635 
   1636  /*
   1637   * Return the total number of bytes read...
   1638   */
   1639 
   1640   DEBUG_printf(("3cupsFileRead: total=" CUPS_LLFMT, CUPS_LLCAST total));
   1641 
   1642   return ((ssize_t)total);
   1643 }
   1644 
   1645 
   1646 /*
   1647  * 'cupsFileRewind()' - Set the current file position to the beginning of the
   1648  *                      file.
   1649  *
   1650  * @since CUPS 1.2/macOS 10.5@
   1651  */
   1652 
   1653 off_t					/* O - New file position or -1 on error */
   1654 cupsFileRewind(cups_file_t *fp)		/* I - CUPS file */
   1655 {
   1656  /*
   1657   * Range check input...
   1658   */
   1659 
   1660   DEBUG_printf(("cupsFileRewind(fp=%p)", (void *)fp));
   1661   DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
   1662 
   1663   if (!fp || fp->mode != 'r')
   1664     return (-1);
   1665 
   1666  /*
   1667   * Handle special cases...
   1668   */
   1669 
   1670   if (fp->bufpos == 0)
   1671   {
   1672    /*
   1673     * No seeking necessary...
   1674     */
   1675 
   1676     fp->pos = 0;
   1677 
   1678     if (fp->ptr)
   1679     {
   1680       fp->ptr = fp->buf;
   1681       fp->eof = 0;
   1682     }
   1683 
   1684     DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
   1685 
   1686     return (0);
   1687   }
   1688 
   1689  /*
   1690   * Otherwise, seek in the file and cleanup any compression buffers...
   1691   */
   1692 
   1693 #ifdef HAVE_LIBZ
   1694   if (fp->compressed)
   1695   {
   1696     inflateEnd(&fp->stream);
   1697     fp->compressed = 0;
   1698   }
   1699 #endif /* HAVE_LIBZ */
   1700 
   1701   if (lseek(fp->fd, 0, SEEK_SET))
   1702   {
   1703     DEBUG_printf(("1cupsFileRewind: lseek failed: %s", strerror(errno)));
   1704     return (-1);
   1705   }
   1706 
   1707   fp->bufpos = 0;
   1708   fp->pos    = 0;
   1709   fp->ptr    = NULL;
   1710   fp->end    = NULL;
   1711   fp->eof    = 0;
   1712 
   1713   DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
   1714 
   1715   return (0);
   1716 }
   1717 
   1718 
   1719 /*
   1720  * 'cupsFileSeek()' - Seek in a file.
   1721  *
   1722  * @since CUPS 1.2/macOS 10.5@
   1723  */
   1724 
   1725 off_t					/* O - New file position or -1 on error */
   1726 cupsFileSeek(cups_file_t *fp,		/* I - CUPS file */
   1727              off_t       pos)		/* I - Position in file */
   1728 {
   1729   ssize_t	bytes;			/* Number bytes in buffer */
   1730 
   1731 
   1732   DEBUG_printf(("cupsFileSeek(fp=%p, pos=" CUPS_LLFMT ")", (void *)fp, CUPS_LLCAST pos));
   1733   DEBUG_printf(("2cupsFileSeek: fp->pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
   1734   DEBUG_printf(("2cupsFileSeek: fp->ptr=%p, fp->end=%p", (void *)fp->ptr, (void *)fp->end));
   1735 
   1736  /*
   1737   * Range check input...
   1738   */
   1739 
   1740   if (!fp || pos < 0 || fp->mode != 'r')
   1741     return (-1);
   1742 
   1743  /*
   1744   * Handle special cases...
   1745   */
   1746 
   1747   if (pos == 0)
   1748     return (cupsFileRewind(fp));
   1749 
   1750   if (fp->ptr)
   1751   {
   1752     bytes = (ssize_t)(fp->end - fp->buf);
   1753 
   1754     DEBUG_printf(("2cupsFileSeek: bytes=" CUPS_LLFMT, CUPS_LLCAST bytes));
   1755 
   1756     if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
   1757     {
   1758      /*
   1759       * No seeking necessary...
   1760       */
   1761 
   1762       fp->pos = pos;
   1763       fp->ptr = fp->buf + pos - fp->bufpos;
   1764       fp->eof = 0;
   1765 
   1766       return (pos);
   1767     }
   1768   }
   1769 
   1770 #ifdef HAVE_LIBZ
   1771   if (!fp->compressed && !fp->ptr)
   1772   {
   1773    /*
   1774     * Preload a buffer to determine whether the file is compressed...
   1775     */
   1776 
   1777     if (cups_fill(fp) <= 0)
   1778       return (-1);
   1779   }
   1780 #endif /* HAVE_LIBZ */
   1781 
   1782  /*
   1783   * Seek forwards or backwards...
   1784   */
   1785 
   1786   fp->eof = 0;
   1787 
   1788   if (pos < fp->bufpos)
   1789   {
   1790    /*
   1791     * Need to seek backwards...
   1792     */
   1793 
   1794     DEBUG_puts("2cupsFileSeek: SEEK BACKWARDS");
   1795 
   1796 #ifdef HAVE_LIBZ
   1797     if (fp->compressed)
   1798     {
   1799       inflateEnd(&fp->stream);
   1800 
   1801       lseek(fp->fd, 0, SEEK_SET);
   1802       fp->bufpos = 0;
   1803       fp->pos    = 0;
   1804       fp->ptr    = NULL;
   1805       fp->end    = NULL;
   1806 
   1807       while ((bytes = cups_fill(fp)) > 0)
   1808         if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
   1809 	  break;
   1810 
   1811       if (bytes <= 0)
   1812         return (-1);
   1813 
   1814       fp->ptr = fp->buf + pos - fp->bufpos;
   1815       fp->pos = pos;
   1816     }
   1817     else
   1818 #endif /* HAVE_LIBZ */
   1819     {
   1820       fp->bufpos = lseek(fp->fd, pos, SEEK_SET);
   1821       fp->pos    = fp->bufpos;
   1822       fp->ptr    = NULL;
   1823       fp->end    = NULL;
   1824 
   1825       DEBUG_printf(("2cupsFileSeek: lseek() returned " CUPS_LLFMT,
   1826                     CUPS_LLCAST fp->pos));
   1827     }
   1828   }
   1829   else
   1830   {
   1831    /*
   1832     * Need to seek forwards...
   1833     */
   1834 
   1835     DEBUG_puts("2cupsFileSeek: SEEK FORWARDS");
   1836 
   1837 #ifdef HAVE_LIBZ
   1838     if (fp->compressed)
   1839     {
   1840       while ((bytes = cups_fill(fp)) > 0)
   1841       {
   1842         if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
   1843 	  break;
   1844       }
   1845 
   1846       if (bytes <= 0)
   1847         return (-1);
   1848 
   1849       fp->ptr = fp->buf + pos - fp->bufpos;
   1850       fp->pos = pos;
   1851     }
   1852     else
   1853 #endif /* HAVE_LIBZ */
   1854     {
   1855       fp->bufpos = lseek(fp->fd, pos, SEEK_SET);
   1856       fp->pos    = fp->bufpos;
   1857       fp->ptr    = NULL;
   1858       fp->end    = NULL;
   1859 
   1860       DEBUG_printf(("2cupsFileSeek: lseek() returned " CUPS_LLFMT,
   1861                     CUPS_LLCAST fp->pos));
   1862     }
   1863   }
   1864 
   1865   DEBUG_printf(("2cupsFileSeek: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
   1866 
   1867   return (fp->pos);
   1868 }
   1869 
   1870 
   1871 /*
   1872  * 'cupsFileStderr()' - Return a CUPS file associated with stderr.
   1873  *
   1874  * @since CUPS 1.2/macOS 10.5@
   1875  */
   1876 
   1877 cups_file_t *				/* O - CUPS file */
   1878 cupsFileStderr(void)
   1879 {
   1880   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals... */
   1881 
   1882 
   1883  /*
   1884   * Open file descriptor 2 as needed...
   1885   */
   1886 
   1887   if (!cg->stdio_files[2])
   1888   {
   1889    /*
   1890     * Flush any pending output on the stdio file...
   1891     */
   1892 
   1893     fflush(stderr);
   1894 
   1895    /*
   1896     * Open file descriptor 2...
   1897     */
   1898 
   1899     if ((cg->stdio_files[2] = cupsFileOpenFd(2, "w")) != NULL)
   1900       cg->stdio_files[2]->is_stdio = 1;
   1901   }
   1902 
   1903   return (cg->stdio_files[2]);
   1904 }
   1905 
   1906 
   1907 /*
   1908  * 'cupsFileStdin()' - Return a CUPS file associated with stdin.
   1909  *
   1910  * @since CUPS 1.2/macOS 10.5@
   1911  */
   1912 
   1913 cups_file_t *				/* O - CUPS file */
   1914 cupsFileStdin(void)
   1915 {
   1916   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals... */
   1917 
   1918 
   1919  /*
   1920   * Open file descriptor 0 as needed...
   1921   */
   1922 
   1923   if (!cg->stdio_files[0])
   1924   {
   1925    /*
   1926     * Open file descriptor 0...
   1927     */
   1928 
   1929     if ((cg->stdio_files[0] = cupsFileOpenFd(0, "r")) != NULL)
   1930       cg->stdio_files[0]->is_stdio = 1;
   1931   }
   1932 
   1933   return (cg->stdio_files[0]);
   1934 }
   1935 
   1936 
   1937 /*
   1938  * 'cupsFileStdout()' - Return a CUPS file associated with stdout.
   1939  *
   1940  * @since CUPS 1.2/macOS 10.5@
   1941  */
   1942 
   1943 cups_file_t *				/* O - CUPS file */
   1944 cupsFileStdout(void)
   1945 {
   1946   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals... */
   1947 
   1948 
   1949  /*
   1950   * Open file descriptor 1 as needed...
   1951   */
   1952 
   1953   if (!cg->stdio_files[1])
   1954   {
   1955    /*
   1956     * Flush any pending output on the stdio file...
   1957     */
   1958 
   1959     fflush(stdout);
   1960 
   1961    /*
   1962     * Open file descriptor 1...
   1963     */
   1964 
   1965     if ((cg->stdio_files[1] = cupsFileOpenFd(1, "w")) != NULL)
   1966       cg->stdio_files[1]->is_stdio = 1;
   1967   }
   1968 
   1969   return (cg->stdio_files[1]);
   1970 }
   1971 
   1972 
   1973 /*
   1974  * 'cupsFileTell()' - Return the current file position.
   1975  *
   1976  * @since CUPS 1.2/macOS 10.5@
   1977  */
   1978 
   1979 off_t					/* O - File position */
   1980 cupsFileTell(cups_file_t *fp)		/* I - CUPS file */
   1981 {
   1982   DEBUG_printf(("2cupsFileTell(fp=%p)", (void *)fp));
   1983   DEBUG_printf(("3cupsFileTell: pos=" CUPS_LLFMT, CUPS_LLCAST (fp ? fp->pos : -1)));
   1984 
   1985   return (fp ? fp->pos : 0);
   1986 }
   1987 
   1988 
   1989 /*
   1990  * 'cupsFileUnlock()' - Unlock access to a file.
   1991  *
   1992  * @since CUPS 1.2/macOS 10.5@
   1993  */
   1994 
   1995 int					/* O - 0 on success, -1 on error */
   1996 cupsFileUnlock(cups_file_t *fp)		/* I - CUPS file */
   1997 {
   1998  /*
   1999   * Range check...
   2000   */
   2001 
   2002   DEBUG_printf(("cupsFileUnlock(fp=%p)", (void *)fp));
   2003 
   2004   if (!fp || fp->mode == 's')
   2005     return (-1);
   2006 
   2007  /*
   2008   * Unlock...
   2009   */
   2010 
   2011 #ifdef WIN32
   2012   return (_locking(fp->fd, _LK_UNLCK, 0));
   2013 #else
   2014   return (lockf(fp->fd, F_ULOCK, 0));
   2015 #endif /* WIN32 */
   2016 }
   2017 
   2018 
   2019 /*
   2020  * 'cupsFileWrite()' - Write to a file.
   2021  *
   2022  * @since CUPS 1.2/macOS 10.5@
   2023  */
   2024 
   2025 ssize_t					/* O - Number of bytes written or -1 on error */
   2026 cupsFileWrite(cups_file_t *fp,		/* I - CUPS file */
   2027               const char  *buf,		/* I - Buffer */
   2028 	      size_t      bytes)	/* I - Number of bytes to write */
   2029 {
   2030  /*
   2031   * Range check input...
   2032   */
   2033 
   2034   DEBUG_printf(("2cupsFileWrite(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes));
   2035 
   2036   if (!fp || !buf || (fp->mode != 'w' && fp->mode != 's'))
   2037     return (-1);
   2038 
   2039   if (bytes == 0)
   2040     return (0);
   2041 
   2042  /*
   2043   * Write the buffer...
   2044   */
   2045 
   2046   if (fp->mode == 's')
   2047   {
   2048     if (cups_write(fp, buf, bytes) < 0)
   2049       return (-1);
   2050 
   2051     fp->pos += (off_t)bytes;
   2052 
   2053     DEBUG_printf(("4cupsFileWrite: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
   2054 
   2055     return ((ssize_t)bytes);
   2056   }
   2057 
   2058   if ((fp->ptr + bytes) > fp->end)
   2059     if (cupsFileFlush(fp))
   2060       return (-1);
   2061 
   2062   fp->pos += (off_t)bytes;
   2063 
   2064   DEBUG_printf(("4cupsFileWrite: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
   2065 
   2066   if (bytes > sizeof(fp->buf))
   2067   {
   2068 #ifdef HAVE_LIBZ
   2069     if (fp->compressed)
   2070       return (cups_compress(fp, buf, bytes));
   2071     else
   2072 #endif /* HAVE_LIBZ */
   2073       return (cups_write(fp, buf, bytes));
   2074   }
   2075   else
   2076   {
   2077     memcpy(fp->ptr, buf, bytes);
   2078     fp->ptr += bytes;
   2079     return ((ssize_t)bytes);
   2080   }
   2081 }
   2082 
   2083 
   2084 #ifdef HAVE_LIBZ
   2085 /*
   2086  * 'cups_compress()' - Compress a buffer of data.
   2087  */
   2088 
   2089 static ssize_t				/* O - Number of bytes written or -1 */
   2090 cups_compress(cups_file_t *fp,		/* I - CUPS file */
   2091               const char  *buf,		/* I - Buffer */
   2092 	      size_t      bytes)	/* I - Number bytes */
   2093 {
   2094   DEBUG_printf(("7cups_compress(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes));
   2095 
   2096  /*
   2097   * Update the CRC...
   2098   */
   2099 
   2100   fp->crc = crc32(fp->crc, (const Bytef *)buf, (uInt)bytes);
   2101 
   2102  /*
   2103   * Deflate the bytes...
   2104   */
   2105 
   2106   fp->stream.next_in  = (Bytef *)buf;
   2107   fp->stream.avail_in = (uInt)bytes;
   2108 
   2109   while (fp->stream.avail_in > 0)
   2110   {
   2111    /*
   2112     * Flush the current buffer...
   2113     */
   2114 
   2115     DEBUG_printf(("9cups_compress: avail_in=%d, avail_out=%d",
   2116                   fp->stream.avail_in, fp->stream.avail_out));
   2117 
   2118     if (fp->stream.avail_out < (uInt)(sizeof(fp->cbuf) / 8))
   2119     {
   2120       if (cups_write(fp, (char *)fp->cbuf, (size_t)(fp->stream.next_out - fp->cbuf)) < 0)
   2121         return (-1);
   2122 
   2123       fp->stream.next_out  = fp->cbuf;
   2124       fp->stream.avail_out = sizeof(fp->cbuf);
   2125     }
   2126 
   2127     deflate(&(fp->stream), Z_NO_FLUSH);
   2128   }
   2129 
   2130   return ((ssize_t)bytes);
   2131 }
   2132 #endif /* HAVE_LIBZ */
   2133 
   2134 
   2135 /*
   2136  * 'cups_fill()' - Fill the input buffer.
   2137  */
   2138 
   2139 static ssize_t				/* O - Number of bytes or -1 */
   2140 cups_fill(cups_file_t *fp)		/* I - CUPS file */
   2141 {
   2142   ssize_t		bytes;		/* Number of bytes read */
   2143 #ifdef HAVE_LIBZ
   2144   int			status;		/* Decompression status */
   2145   const unsigned char	*ptr,		/* Pointer into buffer */
   2146 			*end;		/* End of buffer */
   2147 #endif /* HAVE_LIBZ */
   2148 
   2149 
   2150   DEBUG_printf(("7cups_fill(fp=%p)", (void *)fp));
   2151   DEBUG_printf(("9cups_fill: fp->ptr=%p, fp->end=%p, fp->buf=%p, fp->bufpos=" CUPS_LLFMT ", fp->eof=%d", (void *)fp->ptr, (void *)fp->end, (void *)fp->buf, CUPS_LLCAST fp->bufpos, fp->eof));
   2152 
   2153   if (fp->ptr && fp->end)
   2154     fp->bufpos += fp->end - fp->buf;
   2155 
   2156 #ifdef HAVE_LIBZ
   2157   DEBUG_printf(("9cups_fill: fp->compressed=%d", fp->compressed));
   2158 
   2159   while (!fp->ptr || fp->compressed)
   2160   {
   2161    /*
   2162     * Check to see if we have read any data yet; if not, see if we have a
   2163     * compressed file...
   2164     */
   2165 
   2166     if (!fp->ptr)
   2167     {
   2168      /*
   2169       * Reset the file position in case we are seeking...
   2170       */
   2171 
   2172       fp->compressed = 0;
   2173 
   2174      /*
   2175       * Read the first bytes in the file to determine if we have a gzip'd
   2176       * file...
   2177       */
   2178 
   2179       if ((bytes = cups_read(fp, (char *)fp->buf, sizeof(fp->buf))) < 0)
   2180       {
   2181        /*
   2182 	* Can't read from file!
   2183 	*/
   2184 
   2185         DEBUG_printf(("9cups_fill: cups_read() returned " CUPS_LLFMT,
   2186 	              CUPS_LLCAST bytes));
   2187 
   2188         fp->eof = 1;
   2189 
   2190 	return (-1);
   2191       }
   2192 
   2193       if (bytes < 10 || fp->buf[0] != 0x1f ||
   2194           (fp->buf[1] & 255) != 0x8b ||
   2195           fp->buf[2] != 8 || (fp->buf[3] & 0xe0) != 0)
   2196       {
   2197        /*
   2198 	* Not a gzip'd file!
   2199 	*/
   2200 
   2201 	fp->ptr = fp->buf;
   2202 	fp->end = fp->buf + bytes;
   2203 
   2204         DEBUG_printf(("9cups_fill: Returning " CUPS_LLFMT,
   2205 	              CUPS_LLCAST bytes));
   2206 
   2207 	return (bytes);
   2208       }
   2209 
   2210      /*
   2211       * Parse header junk: extra data, original name, and comment...
   2212       */
   2213 
   2214       ptr = (unsigned char *)fp->buf + 10;
   2215       end = (unsigned char *)fp->buf + bytes;
   2216 
   2217       if (fp->buf[3] & 0x04)
   2218       {
   2219        /*
   2220 	* Skip extra data...
   2221 	*/
   2222 
   2223 	if ((ptr + 2) > end)
   2224 	{
   2225 	 /*
   2226 	  * Can't read from file!
   2227 	  */
   2228 
   2229 	  DEBUG_puts("9cups_fill: Extra gzip header data missing, returning -1.");
   2230 
   2231           fp->eof = 1;
   2232 	  errno   = EIO;
   2233 
   2234 	  return (-1);
   2235 	}
   2236 
   2237 	bytes = ((unsigned char)ptr[1] << 8) | (unsigned char)ptr[0];
   2238 	ptr   += 2 + bytes;
   2239 
   2240 	if (ptr > end)
   2241 	{
   2242 	 /*
   2243 	  * Can't read from file!
   2244 	  */
   2245 
   2246 	  DEBUG_puts("9cups_fill: Extra gzip header data does not fit in initial buffer, returning -1.");
   2247 
   2248           fp->eof = 1;
   2249 	  errno   = EIO;
   2250 
   2251 	  return (-1);
   2252 	}
   2253       }
   2254 
   2255       if (fp->buf[3] & 0x08)
   2256       {
   2257        /*
   2258 	* Skip original name data...
   2259 	*/
   2260 
   2261 	while (ptr < end && *ptr)
   2262           ptr ++;
   2263 
   2264 	if (ptr < end)
   2265           ptr ++;
   2266 	else
   2267 	{
   2268 	 /*
   2269 	  * Can't read from file!
   2270 	  */
   2271 
   2272 	  DEBUG_puts("9cups_fill: Original filename in gzip header data does not fit in initial buffer, returning -1.");
   2273 
   2274           fp->eof = 1;
   2275 	  errno   = EIO;
   2276 
   2277 	  return (-1);
   2278 	}
   2279       }
   2280 
   2281       if (fp->buf[3] & 0x10)
   2282       {
   2283        /*
   2284 	* Skip comment data...
   2285 	*/
   2286 
   2287 	while (ptr < end && *ptr)
   2288           ptr ++;
   2289 
   2290 	if (ptr < end)
   2291           ptr ++;
   2292 	else
   2293 	{
   2294 	 /*
   2295 	  * Can't read from file!
   2296 	  */
   2297 
   2298 	  DEBUG_puts("9cups_fill: Comment in gzip header data does not fit in initial buffer, returning -1.");
   2299 
   2300           fp->eof = 1;
   2301 	  errno   = EIO;
   2302 
   2303 	  return (-1);
   2304 	}
   2305       }
   2306 
   2307       if (fp->buf[3] & 0x02)
   2308       {
   2309        /*
   2310 	* Skip header CRC data...
   2311 	*/
   2312 
   2313 	ptr += 2;
   2314 
   2315 	if (ptr > end)
   2316 	{
   2317 	 /*
   2318 	  * Can't read from file!
   2319 	  */
   2320 
   2321 	  DEBUG_puts("9cups_fill: Header CRC in gzip header data does not fit in initial buffer, returning -1.");
   2322 
   2323           fp->eof = 1;
   2324 	  errno   = EIO;
   2325 
   2326 	  return (-1);
   2327 	}
   2328       }
   2329 
   2330      /*
   2331       * Copy the flate-compressed data to the compression buffer...
   2332       */
   2333 
   2334       if ((bytes = end - ptr) > 0)
   2335         memcpy(fp->cbuf, ptr, (size_t)bytes);
   2336 
   2337      /*
   2338       * Setup the decompressor data...
   2339       */
   2340 
   2341       fp->stream.zalloc    = (alloc_func)0;
   2342       fp->stream.zfree     = (free_func)0;
   2343       fp->stream.opaque    = (voidpf)0;
   2344       fp->stream.next_in   = (Bytef *)fp->cbuf;
   2345       fp->stream.next_out  = NULL;
   2346       fp->stream.avail_in  = (uInt)bytes;
   2347       fp->stream.avail_out = 0;
   2348       fp->crc              = crc32(0L, Z_NULL, 0);
   2349 
   2350       if ((status = inflateInit2(&(fp->stream), -15)) != Z_OK)
   2351       {
   2352 	DEBUG_printf(("9cups_fill: inflateInit2 returned %d, returning -1.", status));
   2353 
   2354         fp->eof = 1;
   2355         errno   = EIO;
   2356 
   2357 	return (-1);
   2358       }
   2359 
   2360       fp->compressed = 1;
   2361     }
   2362 
   2363     if (fp->compressed)
   2364     {
   2365      /*
   2366       * If we have reached end-of-file, return immediately...
   2367       */
   2368 
   2369       if (fp->eof)
   2370       {
   2371         DEBUG_puts("9cups_fill: EOF, returning 0.");
   2372 
   2373 	return (0);
   2374       }
   2375 
   2376      /*
   2377       * Fill the decompression buffer as needed...
   2378       */
   2379 
   2380       if (fp->stream.avail_in == 0)
   2381       {
   2382 	if ((bytes = cups_read(fp, (char *)fp->cbuf, sizeof(fp->cbuf))) <= 0)
   2383 	{
   2384 	  DEBUG_printf(("9cups_fill: cups_read error, returning %d.", (int)bytes));
   2385 
   2386 	  fp->eof = 1;
   2387 
   2388           return (bytes);
   2389 	}
   2390 
   2391 	fp->stream.next_in  = fp->cbuf;
   2392 	fp->stream.avail_in = (uInt)bytes;
   2393       }
   2394 
   2395      /*
   2396       * Decompress data from the buffer...
   2397       */
   2398 
   2399       fp->stream.next_out  = (Bytef *)fp->buf;
   2400       fp->stream.avail_out = sizeof(fp->buf);
   2401 
   2402       status = inflate(&(fp->stream), Z_NO_FLUSH);
   2403 
   2404       if (fp->stream.next_out > (Bytef *)fp->buf)
   2405         fp->crc = crc32(fp->crc, (Bytef *)fp->buf,
   2406 	                (uInt)(fp->stream.next_out - (Bytef *)fp->buf));
   2407 
   2408       if (status == Z_STREAM_END)
   2409       {
   2410        /*
   2411 	* Read the CRC and length...
   2412 	*/
   2413 
   2414 	unsigned char	trailer[8];	/* Trailer bytes */
   2415 	uLong		tcrc;		/* Trailer CRC */
   2416 	ssize_t		tbytes = 0;	/* Number of bytes */
   2417 
   2418 	if (fp->stream.avail_in > 0)
   2419 	{
   2420 	  if (fp->stream.avail_in > sizeof(trailer))
   2421 	    tbytes = (ssize_t)sizeof(trailer);
   2422 	  else
   2423 	    tbytes = (ssize_t)fp->stream.avail_in;
   2424 
   2425 	  memcpy(trailer, fp->stream.next_in, (size_t)tbytes);
   2426 	  fp->stream.next_in  += tbytes;
   2427 	  fp->stream.avail_in -= (size_t)tbytes;
   2428 	}
   2429 
   2430         if (tbytes < (ssize_t)sizeof(trailer))
   2431 	{
   2432 	  if (read(fp->fd, trailer + tbytes, sizeof(trailer) - (size_t)tbytes) < ((ssize_t)sizeof(trailer) - tbytes))
   2433 	  {
   2434 	   /*
   2435 	    * Can't get it, so mark end-of-file...
   2436 	    */
   2437 
   2438 	    DEBUG_puts("9cups_fill: Unable to read gzip CRC trailer, returning -1.");
   2439 
   2440 	    fp->eof = 1;
   2441 	    errno   = EIO;
   2442 
   2443 	    return (-1);
   2444 	  }
   2445 	}
   2446 
   2447 	tcrc = ((((((uLong)trailer[3] << 8) | (uLong)trailer[2]) << 8) |
   2448 		(uLong)trailer[1]) << 8) | (uLong)trailer[0];
   2449 
   2450 	if (tcrc != fp->crc)
   2451 	{
   2452 	 /*
   2453 	  * Bad CRC, mark end-of-file...
   2454 	  */
   2455 
   2456 	  DEBUG_printf(("9cups_fill: tcrc=%08x != fp->crc=%08x, returning -1.", (unsigned int)tcrc, (unsigned int)fp->crc));
   2457 
   2458 	  fp->eof = 1;
   2459 	  errno   = EIO;
   2460 
   2461 	  return (-1);
   2462 	}
   2463 
   2464        /*
   2465 	* Otherwise, reset the compressed flag so that we re-read the
   2466 	* file header...
   2467 	*/
   2468 
   2469 	fp->compressed = 0;
   2470       }
   2471       else if (status < Z_OK)
   2472       {
   2473 	DEBUG_printf(("9cups_fill: inflate returned %d, returning -1.", status));
   2474 
   2475         fp->eof = 1;
   2476         errno   = EIO;
   2477 
   2478 	return (-1);
   2479       }
   2480 
   2481       bytes = (ssize_t)sizeof(fp->buf) - (ssize_t)fp->stream.avail_out;
   2482 
   2483      /*
   2484       * Return the decompressed data...
   2485       */
   2486 
   2487       fp->ptr = fp->buf;
   2488       fp->end = fp->buf + bytes;
   2489 
   2490       if (bytes)
   2491       {
   2492         DEBUG_printf(("9cups_fill: Returning %d.", (int)bytes));
   2493 	return (bytes);
   2494       }
   2495     }
   2496   }
   2497 #endif /* HAVE_LIBZ */
   2498 
   2499  /*
   2500   * Read a buffer's full of data...
   2501   */
   2502 
   2503   if ((bytes = cups_read(fp, fp->buf, sizeof(fp->buf))) <= 0)
   2504   {
   2505    /*
   2506     * Can't read from file!
   2507     */
   2508 
   2509     fp->eof = 1;
   2510     fp->ptr = fp->buf;
   2511     fp->end = fp->buf;
   2512   }
   2513   else
   2514   {
   2515    /*
   2516     * Return the bytes we read...
   2517     */
   2518 
   2519     fp->eof = 0;
   2520     fp->ptr = fp->buf;
   2521     fp->end = fp->buf + bytes;
   2522   }
   2523 
   2524   DEBUG_printf(("9cups_fill: Not gzip, returning %d.", (int)bytes));
   2525 
   2526   return (bytes);
   2527 }
   2528 
   2529 
   2530 /*
   2531  * 'cups_open()' - Safely open a file for writing.
   2532  *
   2533  * We don't allow appending to directories or files that are hard-linked or
   2534  * symlinked.
   2535  */
   2536 
   2537 static int				/* O - File descriptor or -1 otherwise */
   2538 cups_open(const char *filename,		/* I - Filename */
   2539 	  int        mode)		/* I - Open mode */
   2540 {
   2541   int		fd;			/* File descriptor */
   2542   struct stat	fileinfo;		/* File information */
   2543 #ifndef WIN32
   2544   struct stat	linkinfo;		/* Link information */
   2545 #endif /* !WIN32 */
   2546 
   2547 
   2548  /*
   2549   * Open the file...
   2550   */
   2551 
   2552   if ((fd = open(filename, mode, 0666)) < 0)
   2553     return (-1);
   2554 
   2555  /*
   2556   * Then verify that the file descriptor doesn't point to a directory or hard-
   2557   * linked file.
   2558   */
   2559 
   2560   if (fstat(fd, &fileinfo))
   2561   {
   2562     close(fd);
   2563     return (-1);
   2564   }
   2565 
   2566   if (fileinfo.st_nlink != 1)
   2567   {
   2568     close(fd);
   2569     errno = EPERM;
   2570     return (-1);
   2571   }
   2572 
   2573 #ifdef WIN32
   2574   if (fileinfo.st_mode & _S_IFDIR)
   2575 #else
   2576   if (S_ISDIR(fileinfo.st_mode))
   2577 #endif /* WIN32 */
   2578   {
   2579     close(fd);
   2580     errno = EISDIR;
   2581     return (-1);
   2582   }
   2583 
   2584 #ifndef WIN32
   2585  /*
   2586   * Then use lstat to determine whether the filename is a symlink...
   2587   */
   2588 
   2589   if (lstat(filename, &linkinfo))
   2590   {
   2591     close(fd);
   2592     return (-1);
   2593   }
   2594 
   2595   if (S_ISLNK(linkinfo.st_mode) ||
   2596       fileinfo.st_dev != linkinfo.st_dev ||
   2597       fileinfo.st_ino != linkinfo.st_ino ||
   2598 #ifdef HAVE_ST_GEN
   2599       fileinfo.st_gen != linkinfo.st_gen ||
   2600 #endif /* HAVE_ST_GEN */
   2601       fileinfo.st_nlink != linkinfo.st_nlink ||
   2602       fileinfo.st_mode != linkinfo.st_mode)
   2603   {
   2604    /*
   2605     * Yes, don't allow!
   2606     */
   2607 
   2608     close(fd);
   2609     errno = EPERM;
   2610     return (-1);
   2611   }
   2612 #endif /* !WIN32 */
   2613 
   2614   return (fd);
   2615 }
   2616 
   2617 
   2618 /*
   2619  * 'cups_read()' - Read from a file descriptor.
   2620  */
   2621 
   2622 static ssize_t				/* O - Number of bytes read or -1 */
   2623 cups_read(cups_file_t *fp,		/* I - CUPS file */
   2624           char        *buf,		/* I - Buffer */
   2625 	  size_t      bytes)		/* I - Number bytes */
   2626 {
   2627   ssize_t	total;			/* Total bytes read */
   2628 
   2629 
   2630   DEBUG_printf(("7cups_read(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes));
   2631 
   2632  /*
   2633   * Loop until we read at least 0 bytes...
   2634   */
   2635 
   2636   for (;;)
   2637   {
   2638 #ifdef WIN32
   2639     if (fp->mode == 's')
   2640       total = (ssize_t)recv(fp->fd, buf, (unsigned)bytes, 0);
   2641     else
   2642       total = (ssize_t)read(fp->fd, buf, (unsigned)bytes);
   2643 #else
   2644     if (fp->mode == 's')
   2645       total = recv(fp->fd, buf, bytes, 0);
   2646     else
   2647       total = read(fp->fd, buf, bytes);
   2648 #endif /* WIN32 */
   2649 
   2650     DEBUG_printf(("9cups_read: total=" CUPS_LLFMT, CUPS_LLCAST total));
   2651 
   2652     if (total >= 0)
   2653       break;
   2654 
   2655    /*
   2656     * Reads can be interrupted by signals and unavailable resources...
   2657     */
   2658 
   2659     if (errno == EAGAIN || errno == EINTR)
   2660       continue;
   2661     else
   2662       return (-1);
   2663   }
   2664 
   2665  /*
   2666   * Return the total number of bytes read...
   2667   */
   2668 
   2669   return (total);
   2670 }
   2671 
   2672 
   2673 /*
   2674  * 'cups_write()' - Write to a file descriptor.
   2675  */
   2676 
   2677 static ssize_t				/* O - Number of bytes written or -1 */
   2678 cups_write(cups_file_t *fp,		/* I - CUPS file */
   2679            const char  *buf,		/* I - Buffer */
   2680 	   size_t      bytes)		/* I - Number bytes */
   2681 {
   2682   size_t	total;			/* Total bytes written */
   2683   ssize_t	count;			/* Count this time */
   2684 
   2685 
   2686   DEBUG_printf(("7cups_write(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes));
   2687 
   2688  /*
   2689   * Loop until all bytes are written...
   2690   */
   2691 
   2692   total = 0;
   2693   while (bytes > 0)
   2694   {
   2695 #ifdef WIN32
   2696     if (fp->mode == 's')
   2697       count = (ssize_t)send(fp->fd, buf, (unsigned)bytes, 0);
   2698     else
   2699       count = (ssize_t)write(fp->fd, buf, (unsigned)bytes);
   2700 #else
   2701     if (fp->mode == 's')
   2702       count = send(fp->fd, buf, bytes, 0);
   2703     else
   2704       count = write(fp->fd, buf, bytes);
   2705 #endif /* WIN32 */
   2706 
   2707     DEBUG_printf(("9cups_write: count=" CUPS_LLFMT, CUPS_LLCAST count));
   2708 
   2709     if (count < 0)
   2710     {
   2711      /*
   2712       * Writes can be interrupted by signals and unavailable resources...
   2713       */
   2714 
   2715       if (errno == EAGAIN || errno == EINTR)
   2716         continue;
   2717       else
   2718         return (-1);
   2719     }
   2720 
   2721    /*
   2722     * Update the counts for the last write call...
   2723     */
   2724 
   2725     bytes -= (size_t)count;
   2726     total += (size_t)count;
   2727     buf   += count;
   2728   }
   2729 
   2730  /*
   2731   * Return the total number of bytes written...
   2732   */
   2733 
   2734   return ((ssize_t)total);
   2735 }
   2736