/* * "$Id: file.c,v 1.8 2005/01/04 22:10:45 jlovell Exp $" * * File functions for the Common UNIX Printing System (CUPS). * * Since stdio files max out at 256 files on many systems, we have to * write similar functions without this limit. At the same time, using * our own file functions allows us to provide transparent support of * gzip'd print files, PPD files, etc. * * Copyright 1997-2005 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the * property of Easy Software Products and are protected by Federal * copyright law. Distribution and use rights are outlined in the file * "LICENSE.txt" which should have been included with this file. If this * file is missing or damaged please contact Easy Software Products * at: * * Attn: CUPS Licensing Information * Easy Software Products * 44141 Airport View Drive, Suite 204 * Hollywood, Maryland 20636 USA * * Voice: (301) 373-9600 * EMail: cups-info@cups.org * WWW: http://www.cups.org * * Contents: * * cupsFileClose() - Close a CUPS file. * cupsFileFlush() - Flush pending output. * cupsFileGetChar() - Get a single character from a file. * cupsFileGets() - Get a CR and/or LF-terminated line. * cupsFileOpen() - Open a CUPS file. * cupsFilePrintf() - Write a formatted string. * cupsFilePutChar() - Write a character. * cupsFilePuts() - Write a string. * cupsFileRead() - Read from a file. * cupsFileSeek() - Seek in a file. * cupsFileWrite() - Write to a file. * cups_fill() - Fill the input buffer... * cups_read() - Read from a file descriptor. * cups_write() - Write to a file descriptor. */ /* * Include necessary headers... */ #include #include #include #include #include #ifdef WIN32 # include #else # include # include #endif /* WIN32 */ #include "file.h" /* * Local functions... */ static int cups_fill(cups_file_t *fp); static int cups_read(int fd, char *buf, int bytes); static int cups_write(int fd, const char *buf, int bytes); /* * 'cupsFileClose()' - Close a CUPS file. */ int /* O - 0 on success, -1 on error */ cupsFileClose(cups_file_t *fp) /* I - CUPS file */ { int fd; /* File descriptor */ /* * Range check... */ if (!fp) return (-1); #ifdef HAVE_LIBZ /* * Free decompression data as needed... */ if (fp->compressed && fp->mode == 'r') inflateEnd(&fp->stream); #endif /* HAVE_LIBZ */ if (fp->mode == 'w') cupsFileFlush(fp); /* * Save the file descriptor we used and free memory... */ fd = fp->fd; free(fp); /* * Close the file, returning the close status... */ return (close(fd)); } /* * 'cupsFileFlush()' - Flush pending output. */ int /* O - 0 on success, -1 on error */ cupsFileFlush(cups_file_t *fp) /* I - CUPS file */ { int bytes; /* Bytes to write */ /* * Range check input... */ if (!fp || fp->mode != 'w') return (-1); bytes = fp->ptr - fp->buf; if (bytes > 0) { if (cups_write(fp->fd, fp->buf, bytes) < bytes) return (-1); fp->ptr = fp->buf; } return (0); } /* * 'cupsFileGetChar()' - Get a single character from a file. */ int /* O - Character or -1 on EOF */ cupsFileGetChar(cups_file_t *fp) /* I - CUPS file */ { /* * Range check input... */ if (!fp || fp->mode != 'r') return (-1); /* * If the input buffer is empty, try to read more data... */ if (fp->ptr >= fp->end) if (cups_fill(fp) < 0) return (-1); /* * Return the next character in the buffer... */ return (*(fp->ptr)++ & 255); } /* * 'cupsFileGets()' - Get a CR and/or LF-terminated line. */ char * /* O - Line read or NULL on eof/error */ cupsFileGets(cups_file_t *fp, /* I - CUPS file */ char *buf, /* O - String buffer */ int buflen) /* I - Size of string buffer */ { int ch; /* Character from file */ char *ptr, /* Current position in line buffer */ *end; /* End of line buffer */ /* * Range check input... */ if (!fp || fp->mode != 'r' || !buf || buflen < 2) return (NULL); /* * Now loop until we have a valid line... */ for (ptr = buf, end = buf + buflen - 1; ptr < end ;) { if (fp->ptr >= fp->end) if (cups_fill(fp) <= 0) { if (ptr == buf) return (NULL); else break; } ch = *(fp->ptr)++; if (ch == '\r') { /* * Check for CR LF... */ if (fp->ptr >= fp->end) if (cups_fill(fp) <= 0) break; if (*(fp->ptr) == '\n') fp->ptr ++; break; } else if (ch == '\n') { /* * Line feed ends a line... */ break; } else *ptr++ = ch; } *ptr = '\0'; return (buf); } /* * 'cupsFileOpen()' - Open a CUPS file. */ cups_file_t * /* O - CUPS file or NULL */ cupsFileOpen(const char *filename, /* I - Name of file */ const char *mode) /* I - Open mode */ { cups_file_t *fp; /* New CUPS file */ int o; /* Open mode bits */ /* * Range check input... */ if (!filename || !mode || (*mode != 'r' && *mode != 'w' && *mode != 'a')) return (NULL); /* * Allocate memory... */ if ((fp = calloc(1, sizeof(cups_file_t))) == NULL) return (NULL); /* * Open the file... */ switch (*mode) { case 'a' : o = O_RDWR | O_CREAT; fp->mode = 'w'; break; case 'r' : o = O_RDONLY; fp->mode = 'r'; break; case 'w' : o = O_WRONLY | O_TRUNC | O_CREAT; fp->mode = 'w'; break; default : /* Remove bogus compiler warning... */ return (NULL); } if ((fp->fd = open(filename, o, 0644)) < 0) { /* * Can't open file! */ free(fp); return (NULL); } /* * Don't pass this file to child processes... */ fcntl(fp->fd, F_SETFD, fcntl(fp->fd, F_GETFD) | FD_CLOEXEC); if (*mode == 'a') fp->pos = lseek(fp->fd, 0, SEEK_END); else fp->pos = 0; if (*mode != 'r') { fp->ptr = fp->buf; fp->end = fp->buf + sizeof(fp->buf); } return (fp); } /* * 'cupsFilePrintf()' - Write a formatted string. */ int /* O - Number of bytes written or -1 */ cupsFilePrintf(cups_file_t *fp, /* I - CUPS file */ const char *format, /* I - Printf-style format string */ ...) /* I - Additional args as necessary */ { va_list ap; /* Argument list */ int bytes; /* Formatted size */ char buf[2048]; /* Formatted text */ if (!fp || !format || fp->mode != 'w') return (-1); va_start(ap, format); bytes = vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); if ((fp->ptr + bytes) > fp->end) if (cupsFileFlush(fp)) return (-1); fp->pos += bytes; if (bytes > sizeof(fp->buf)) return (cups_write(fp->fd, buf, bytes)); else { memcpy(fp->ptr, buf, bytes); fp->ptr += bytes; return (bytes); } } /* * 'cupsFilePutChar()' - Write a character. */ int /* O - 0 on success, -1 on error */ cupsFilePutChar(cups_file_t *fp, /* I - CUPS file */ int c) /* I - Character to write */ { /* * Range check input... */ if (!fp || fp->mode != 'w') return (-1); /* * Buffer it up... */ if (fp->ptr >= fp->end) if (cupsFileFlush(fp)) return (-1); *(fp->ptr) ++ = c; fp->pos ++; return (0); } /* * 'cupsFilePuts()' - Write a string. */ int /* O - Number of bytes written or -1 */ cupsFilePuts(cups_file_t *fp, /* I - CUPS file */ const char *s) /* I - String to write */ { int bytes; /* Bytes to write */ /* * Range check input... */ if (!fp || !s || fp->mode != 'w') return (-1); /* * Write the string... */ bytes = strlen(s); if ((fp->ptr + bytes) > fp->end) if (cupsFileFlush(fp)) return (-1); fp->pos += bytes; if (bytes > sizeof(fp->buf)) return (cups_write(fp->fd, s, bytes)); else { memcpy(fp->ptr, s, bytes); fp->ptr += bytes; return (bytes); } } /* * 'cupsFileRead()' - Read from a file. */ int /* O - Number of bytes read or -1 */ cupsFileRead(cups_file_t *fp, /* I - CUPS file */ char *buf, /* O - Buffer */ int bytes) /* I - Number of bytes to read */ { int total, /* Total bytes read */ count; /* Bytes read */ /* * Range check input... */ if (!fp || !buf || bytes < 0 || fp->mode != 'r') return (-1); if (bytes == 0) return (0); /* * Loop until all bytes are read... */ total = 0; while (bytes > 0) { if (fp->ptr >= fp->end) if (cups_fill(fp) <= 0) { if (total > 0) return (total); else return (-1); } count = fp->end - fp->ptr; if (count > bytes) count = bytes; memcpy(buf, fp->ptr, count); fp->ptr += count; /* * Update the counts for the last read... */ bytes -= count; total += count; buf += count; } /* * Return the total number of bytes read... */ return (total); } /* * 'cupsFileSeek()' - Seek in a file. */ off_t /* O - New file position or -1 */ cupsFileSeek(cups_file_t *fp, /* I - CUPS file */ off_t pos) /* I - Position in file */ { int bytes; /* Number bytes in buffer */ /* * Range check input... */ if (!fp || pos < 0 || fp->mode != 'r') return (-1); /* * Figure out the number of bytes in the current buffer, and then * see if we are outside of it... */ bytes = fp->end - fp->buf; if (pos < fp->pos) { /* * Need to seek backwards... */ #ifdef HAVE_LIBZ if (fp->compressed) { inflateEnd(&fp->stream); lseek(fp->fd, 0, SEEK_SET); fp->pos = 0; fp->ptr = NULL; fp->end = NULL; fp->eof = 0; while ((bytes = cups_fill(fp)) > 0) if (pos >= fp->pos && pos < (fp->pos + bytes)) break; if (bytes <= 0) return (-1); } else #endif /* HAVE_LIBZ */ { lseek(fp->fd, pos, SEEK_SET); fp->pos = pos; fp->ptr = NULL; fp->end = NULL; #ifdef HAVE_LIBZ fp->eof = 0; #endif /* HAVE_LIBZ */ } } else if (pos >= (fp->pos + bytes)) { /* * Need to seek forwards... */ #ifdef HAVE_LIBZ if (fp->compressed) { while ((bytes = cups_fill(fp)) > 0) if (pos >= fp->pos && pos < (fp->pos + bytes)) break; if (bytes <= 0) return (-1); } else #endif /* HAVE_LIBZ */ { lseek(fp->fd, pos, SEEK_SET); fp->pos = pos; fp->ptr = NULL; fp->end = NULL; #ifdef HAVE_LIBZ fp->eof = 0; #endif /* HAVE_LIBZ */ } } else { /* * Just reposition the current pointer, since we have the right * range... */ fp->ptr = fp->buf + pos - fp->pos; #ifdef HAVE_LIBZ fp->eof = 0; #endif /* HAVE_LIBZ */ } return (pos); } /* * 'cupsFileWrite()' - Write to a file. */ int /* O - Number of bytes written */ cupsFileWrite(cups_file_t *fp, /* I - CUPS file */ const char *buf, /* I - Buffer */ int bytes) /* I - Number of bytes to write */ { /* * Range check input... */ if (!fp || !buf || bytes < 0 || fp->mode != 'w') return (-1); if (bytes == 0) return (0); /* * Write the buffer... */ if ((fp->ptr + bytes) > fp->end) if (cupsFileFlush(fp)) return (-1); fp->pos += bytes; if (bytes > sizeof(fp->buf)) return (cups_write(fp->fd, buf, bytes)); else { memcpy(fp->ptr, buf, bytes); fp->ptr += bytes; return (bytes); } } /* * 'cups_fill()' - Fill the input buffer... */ static int /* O - Number of bytes or -1 */ cups_fill(cups_file_t *fp) /* I - CUPS file */ { int bytes; /* Number of bytes read */ #ifdef HAVE_LIBZ const unsigned char *ptr, /* Pointer into buffer */ *end; /* End of buffer */ #endif /* HAVE_LIBZ */ /* * Update the "pos" element as needed... */ if (fp->ptr) fp->pos += fp->end - fp->buf; #ifdef HAVE_LIBZ /* * Check to see if we have read any data yet; if not, see if we have a * compressed file... */ if (!fp->ptr) { /* * Reset the file position in case we are seeking... */ fp->compressed = 0; fp->pos = 0; /* * Read the first bytes in the file to determine if we have a gzip'd * file... */ if ((bytes = cups_read(fp->fd, (char *)fp->buf, sizeof(fp->buf))) < 0) { /* * Can't read from file! */ return (-1); } if (bytes < 10 || fp->buf[0] != 0x1f || fp->buf[1] != 0x8b || fp->buf[2] != 8 || (fp->buf[3] & 0xe0) != 0) { /* * Not a gzip'd file! */ fp->ptr = fp->buf; fp->end = fp->buf + bytes; return (bytes); } /* * Data is compressed; copy as much as will fit to the compression buffer */ if (bytes > sizeof(fp->cbuf)) bytes = sizeof(fp->cbuf); memcpy(fp->cbuf, fp->buf, bytes); lseek(fp->fd, bytes, SEEK_SET); /* * Parse header junk: extra data, original name, and comment... */ ptr = fp->cbuf + 10; end = fp->cbuf + bytes; if (fp->cbuf[3] & 0x04) { /* * Skip extra data... */ if ((ptr + 2) > end) { /* * Can't read from file! */ return (-1); } bytes = ((unsigned char)ptr[1] << 8) | (unsigned char)ptr[0]; ptr += 2 + bytes; if (ptr > end) { /* * Can't read from file! */ return (-1); } } if (fp->cbuf[3] & 0x08) { /* * Skip original name data... */ while (ptr < end && *ptr) ptr ++; if (ptr < end) ptr ++; else { /* * Can't read from file! */ return (-1); } } if (fp->cbuf[3] & 0x10) { /* * Skip comment data... */ while (ptr < end && *ptr) ptr ++; if (ptr < end) ptr ++; else { /* * Can't read from file! */ return (-1); } } if (fp->cbuf[3] & 0x02) { /* * Skip header CRC data... */ ptr += 2; if (ptr > end) { /* * Can't read from file! */ return (-1); } } /* * Setup the decompressor data... */ fp->stream.zalloc = (alloc_func)0; fp->stream.zfree = (free_func)0; fp->stream.opaque = (voidpf)0; fp->stream.next_in = (Bytef *)ptr; fp->stream.next_out = NULL; fp->stream.avail_in = end - ptr; fp->stream.avail_out = 0; if (inflateInit2(&(fp->stream), -15) != Z_OK) return (-1); fp->compressed = 1; } if (fp->compressed) { /* * If we have reached end-of-file, return immediately... */ if (fp->eof) return (-1); /* * Fill the decompression buffer as needed... */ if (fp->stream.avail_in == 0) { if ((bytes = cups_read(fp->fd, (char *)fp->cbuf, sizeof(fp->cbuf))) <= 0) return (-1); fp->stream.next_in = fp->cbuf; fp->stream.avail_in = bytes; } /* * Decompress data from the buffer... */ fp->stream.next_out = (Bytef *)fp->buf; fp->stream.avail_out = sizeof(fp->buf); if (inflate(&(fp->stream), Z_NO_FLUSH) == Z_STREAM_END) { /* * Mark end-of-file; note: we do not support concatenated gzip files * like gunzip does... */ fp->eof = 1; } bytes = sizeof(fp->buf) - fp->stream.avail_out; /* * Return the decompressed data... */ fp->ptr = fp->buf; fp->end = fp->buf + bytes; return (bytes); } #endif /* HAVE_LIBZ */ /* * Read a buffer's full of data... */ if ((bytes = cups_read(fp->fd, fp->buf, sizeof(fp->buf))) <= 0) { /* * Can't read from file! */ fp->ptr = fp->buf; fp->end = fp->buf; return (-1); } /* * Return the bytes we read... */ fp->ptr = fp->buf; fp->end = fp->buf + bytes; return (bytes); } /* * 'cups_read()' - Read from a file descriptor. */ int /* O - Number of bytes read or -1 */ cups_read(int fd, /* I - File descriptor */ char *buf, /* I - Buffer */ int bytes) /* I - Number bytes */ { int total; /* Total bytes read */ /* * Loop until we read at least 0 bytes... */ while ((total = read(fd, buf, bytes)) < 0) { /* * Reads can be interrupted by signals and unavailable resources... */ if (errno == EAGAIN || errno == EINTR) continue; else return (-1); } /* * Return the total number of bytes read... */ return (total); } /* * 'cups_write()' - Write to a file descriptor. */ int /* O - Number of bytes written or -1 */ cups_write(int fd, /* I - File descriptor */ const char *buf, /* I - Buffer */ int bytes) /* I - Number bytes */ { int total, /* Total bytes written */ count; /* Count this time */ /* * Loop until all bytes are written... */ total = 0; while (bytes > 0) { if ((count = write(fd, buf, bytes)) < 0) { /* * Writes can be interrupted by signals and unavailable resources... */ if (errno == EAGAIN || errno == EINTR) continue; else return (-1); } /* * Update the counts for the last write call... */ bytes -= count; total += count; buf += count; } /* * Return the total number of bytes written... */ return (total); } /* * End of "$Id: file.c,v 1.8 2005/01/04 22:10:45 jlovell Exp $". */