ircu2/ircd/fileio.c

239 lines
5.6 KiB
C

/*
* IRC - Internet Relay Chat, ircd/fileio.c
* Copyright (C) 1998 Thomas Helvey <tomh@inxpress.net>
* 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 <assert.h> -- Now using assert in ircd_log.h */ /* assert */
#include <fcntl.h> /* O_RDONLY, O_WRONLY, ... */
#include <stdio.h> /* BUFSIZ, EOF */
#include <sys/stat.h> /* struct stat */
#include <unistd.h> /* read, write, open, close */
#include <string.h>
#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);
}