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