/* * IRC - Internet Relay Chat, ircd/fileio.c * Copyright (C) 1998 Thomas Helvey * Copyright (C) 1990 Jarkko Oikarinen and * University of Oulu, Co Center * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** @file * @brief ANSI FILE* clone API implementation. * @version $Id: fileio.c 1334 2005-03-20 16:06:30Z entrope $ */ #include "config.h" #include "fileio.h" #include "ircd_alloc.h" /* MyMalloc, MyFree */ #include "ircd_log.h" /* assert */ /* #include -- Now using assert in ircd_log.h */ /* assert */ #include /* O_RDONLY, O_WRONLY, ... */ #include /* BUFSIZ, EOF */ #include /* struct stat */ #include /* read, write, open, close */ #include #define FB_EOF 0x01 /**< File has reached EOF. */ #define FB_FAIL 0x02 /**< File operation failed. */ /** Tracks status and buffer for a file on disk. */ struct FileBuf { int fd; /**< file descriptor */ char *endp; /**< one past the end */ char *ptr; /**< current read pos */ int flags; /**< file state */ char buf[BUFSIZ]; /**< buffer */ }; /** Open a new FBFILE. * @param[in] filename Name of file to open. * @param[in] mode fopen()-style mode string. * @return Pointer to newly allocated FBFILE. */ FBFILE* fbopen(const char *filename, const char *mode) { int openmode = 0; int pmode = 0; FBFILE *fb = NULL; int fd; assert(filename); assert(mode); while (*mode) { switch (*mode) { case 'r': openmode = O_RDONLY; break; case 'w': openmode = O_WRONLY | O_CREAT | O_TRUNC; pmode = S_IRUSR | S_IWUSR; break; case 'a': openmode = O_WRONLY | O_CREAT | O_APPEND; pmode = S_IRUSR | S_IWUSR; break; case '+': openmode &= ~(O_RDONLY | O_WRONLY); openmode |= O_RDWR; break; default: break; } ++mode; } /* * stop NFS hangs...most systems should be able to open a file in * 3 seconds. -avalon (courtesy of wumpus) */ alarm(3); if ((fd = open(filename, openmode, pmode)) == -1) { alarm(0); return fb; } alarm(0); if (NULL == (fb = fdbopen(fd, NULL))) close(fd); return fb; } /** Open a FBFILE from a file descriptor. * @param[in] fd File descriptor to use. * @param[in] mode fopen()-style mode string (ignored). */ FBFILE* fdbopen(int fd, const char *mode) { /* * ignore mode, if file descriptor hasn't been opened with the * correct mode, the first use will fail */ FBFILE *fb = (FBFILE *) MyMalloc(sizeof(FBFILE)); assert(0 != fb); fb->ptr = fb->endp = fb->buf; fb->fd = fd; fb->flags = 0; return fb; } /** Close a FBFILE. * @param[in] fb File buffer to close. */ void fbclose(FBFILE* fb) { assert(fb); close(fb->fd); MyFree(fb); } /** Attempt to fill a file's buffer. * @param[in] fb File to operate on. * @return Number of bytes read into buffer, or a negative number on error. */ static int fbfill(FBFILE * fb) { int n; assert(fb); if (fb->flags) return -1; n = read(fb->fd, fb->buf, BUFSIZ); if (0 < n) { fb->ptr = fb->buf; fb->endp = fb->buf + n; } else if (n < 0) fb->flags |= FB_FAIL; else fb->flags |= FB_EOF; return n; } /** Get a single character from a file. * @param[in] fb File to fetch from. * @return Character value read, or EOF on error or end-of-file. */ int fbgetc(FBFILE * fb) { assert(fb); if (fb->ptr < fb->endp || fbfill(fb) > 0) return *fb->ptr++; return EOF; } /** Get a line of input from a file. * @param[out] buf Output buffer to read to. * @param[in] len Maximum number of bytes to write to buffer * (including terminating NUL). * @param[in] fb File to read from. */ char *fbgets(char *buf, size_t len, FBFILE * fb) { char *p = buf; assert(buf); assert(fb); assert(0 < len); if (fb->ptr == fb->endp && fbfill(fb) < 1) return 0; --len; while (len--) { *p = *fb->ptr++; if ('\n' == *p) { ++p; break; } /* * deal with CR's */ else if ('\r' == *p) { if (fb->ptr < fb->endp || fbfill(fb) > 0) { if ('\n' == *fb->ptr) ++fb->ptr; } *p++ = '\n'; break; } ++p; if (fb->ptr == fb->endp && fbfill(fb) < 1) break; } *p = '\0'; return buf; } /** Write a string to a file. * @param[in] str String to write to file. * @param[in] fb File to write to. * @return Number of bytes written, or -1 on error. */ int fbputs(const char *str, FBFILE * fb) { int n = -1; assert(str); assert(fb); if (0 == fb->flags) { n = write(fb->fd, str, strlen(str)); if (-1 == n) fb->flags |= FB_FAIL; } return n; } /** Get file status. * @param[out] sb Receives file status. * @param[in] fb File to get status for. * @return Zero on success, -1 on error. */ int fbstat(struct stat *sb, FBFILE * fb) { assert(sb); assert(fb); return fstat(fb->fd, sb); }