ircu2.10.12 pk910 fork
[ircu2.10.12-pk.git] / ircd / fileio.c
1 /*
2  * IRC - Internet Relay Chat, ircd/fileio.c
3  * Copyright (C) 1998 Thomas Helvey <tomh@inxpress.net>
4  * Copyright (C) 1990 Jarkko Oikarinen and
5  *                  University of Oulu, Co Center
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 1, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21 /** @file
22  * @brief ANSI FILE* clone API implementation.
23  * @version $Id: fileio.c 1334 2005-03-20 16:06:30Z entrope $
24  */
25 #include "config.h"
26
27 #include "fileio.h"
28 #include "ircd_alloc.h"         /* MyMalloc, MyFree */
29 #include "ircd_log.h"           /* assert */
30
31 /* #include <assert.h> -- Now using assert in ircd_log.h */       /* assert */
32 #include <fcntl.h>              /* O_RDONLY, O_WRONLY, ... */
33 #include <stdio.h>              /* BUFSIZ, EOF */
34 #include <sys/stat.h>           /* struct stat */
35 #include <unistd.h>             /* read, write, open, close */
36 #include <string.h>
37
38 #define FB_EOF  0x01            /**< File has reached EOF. */
39 #define FB_FAIL 0x02            /**< File operation failed. */
40
41 /** Tracks status and buffer for a file on disk. */
42 struct FileBuf {
43   int fd;                       /**< file descriptor */
44   char *endp;                   /**< one past the end */
45   char *ptr;                    /**< current read pos */
46   int flags;                    /**< file state */
47   char buf[BUFSIZ];             /**< buffer */
48 };
49
50 /** Open a new FBFILE.
51  * @param[in] filename Name of file to open.
52  * @param[in] mode fopen()-style mode string.
53  * @return Pointer to newly allocated FBFILE.
54  */
55 FBFILE* fbopen(const char *filename, const char *mode)
56 {
57   int openmode = 0;
58   int pmode = 0;
59   FBFILE *fb = NULL;
60   int fd;
61   assert(filename);
62   assert(mode);
63
64   while (*mode) {
65     switch (*mode) {
66     case 'r':
67       openmode = O_RDONLY;
68       break;
69     case 'w':
70       openmode = O_WRONLY | O_CREAT | O_TRUNC;
71       pmode = S_IRUSR | S_IWUSR;
72       break;
73     case 'a':
74       openmode = O_WRONLY | O_CREAT | O_APPEND;
75       pmode = S_IRUSR | S_IWUSR;
76       break;
77     case '+':
78       openmode &= ~(O_RDONLY | O_WRONLY);
79       openmode |= O_RDWR;
80       break;
81     default:
82       break;
83     }
84     ++mode;
85   }
86   /*
87    * stop NFS hangs...most systems should be able to open a file in
88    * 3 seconds. -avalon (courtesy of wumpus)
89    */
90   alarm(3);
91   if ((fd = open(filename, openmode, pmode)) == -1) {
92     alarm(0);
93     return fb;
94   }
95   alarm(0);
96
97   if (NULL == (fb = fdbopen(fd, NULL)))
98     close(fd);
99   return fb;
100 }
101
102 /** Open a FBFILE from a file descriptor.
103  * @param[in] fd File descriptor to use.
104  * @param[in] mode fopen()-style mode string (ignored).
105  */
106 FBFILE* fdbopen(int fd, const char *mode)
107 {
108   /*
109    * ignore mode, if file descriptor hasn't been opened with the
110    * correct mode, the first use will fail
111    */
112   FBFILE *fb = (FBFILE *) MyMalloc(sizeof(FBFILE));
113   assert(0 != fb);
114   fb->ptr   = fb->endp = fb->buf;
115   fb->fd    = fd;
116   fb->flags = 0;
117
118   return fb;
119 }
120
121 /** Close a FBFILE.
122  * @param[in] fb File buffer to close.
123  */
124 void fbclose(FBFILE* fb)
125 {
126   assert(fb);
127   close(fb->fd);
128   MyFree(fb);
129 }
130
131 /** Attempt to fill a file's buffer.
132  * @param[in] fb File to operate on.
133  * @return Number of bytes read into buffer, or a negative number on error.
134  */
135 static int fbfill(FBFILE * fb)
136 {
137   int n;
138   assert(fb);
139   if (fb->flags)
140     return -1;
141   n = read(fb->fd, fb->buf, BUFSIZ);
142   if (0 < n)
143   {
144     fb->ptr = fb->buf;
145     fb->endp = fb->buf + n;
146   }
147   else if (n < 0)
148     fb->flags |= FB_FAIL;
149   else
150     fb->flags |= FB_EOF;
151   return n;
152 }
153
154 /** Get a single character from a file.
155  * @param[in] fb File to fetch from.
156  * @return Character value read, or EOF on error or end-of-file.
157  */
158 int fbgetc(FBFILE * fb)
159 {
160   assert(fb);
161   if (fb->ptr < fb->endp || fbfill(fb) > 0)
162     return *fb->ptr++;
163   return EOF;
164 }
165
166 /** Get a line of input from a file.
167  * @param[out] buf Output buffer to read to.
168  * @param[in] len Maximum number of bytes to write to buffer
169  * (including terminating NUL).
170  * @param[in] fb File to read from.
171  */
172 char *fbgets(char *buf, size_t len, FBFILE * fb)
173 {
174   char *p = buf;
175   assert(buf);
176   assert(fb);
177   assert(0 < len);
178
179   if (fb->ptr == fb->endp && fbfill(fb) < 1)
180     return 0;
181   --len;
182   while (len--) {
183     *p = *fb->ptr++;
184     if ('\n' == *p)
185     {
186       ++p;
187       break;
188     }
189     /*
190      * deal with CR's
191      */
192     else if ('\r' == *p) {
193       if (fb->ptr < fb->endp || fbfill(fb) > 0) {
194         if ('\n' == *fb->ptr)
195           ++fb->ptr;
196       }
197       *p++ = '\n';
198       break;
199     }
200     ++p;
201     if (fb->ptr == fb->endp && fbfill(fb) < 1)
202       break;
203   }
204   *p = '\0';
205   return buf;
206 }
207
208 /** Write a string to a file.
209  * @param[in] str String to write to file.
210  * @param[in] fb File to write to.
211  * @return Number of bytes written, or -1 on error.
212  */
213 int fbputs(const char *str, FBFILE * fb)
214 {
215   int n = -1;
216   assert(str);
217   assert(fb);
218
219   if (0 == fb->flags) {
220     n = write(fb->fd, str, strlen(str));
221     if (-1 == n)
222       fb->flags |= FB_FAIL;
223   }
224   return n;
225 }
226
227 /** Get file status.
228  * @param[out] sb Receives file status.
229  * @param[in] fb File to get status for.
230  * @return Zero on success, -1 on error.
231  */
232 int fbstat(struct stat *sb, FBFILE * fb)
233 {
234   assert(sb);
235   assert(fb);
236   return fstat(fb->fd, sb);
237 }
238