nail.1: mail,reply,etc.: some errors depend on *expandaddr*
[s-mailx.git] / fio.c
blob684842a4767dc561137597542b3a9658dbcc0857
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ File operations.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE fio
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 /* line is a buffer with the result of fgets(). Returns the first newline or
43 * the last character read */
44 static size_t _length_of_line(char const *line, size_t linesize);
46 /* Read a line, one character at a time */
47 static char * _fgetline_byone(char **line, size_t *linesize, size_t *llen,
48 FILE *fp, int appendnl, size_t n n_MEMORY_DEBUG_ARGS);
50 /* Workhorse */
51 static bool_t a_file_lock(int fd, enum n_file_lock_type ft, off_t off,
52 off_t len);
54 static size_t
55 _length_of_line(char const *line, size_t linesize)
57 size_t i;
58 NYD2_ENTER;
60 /* Last character is always '\0' and was added by fgets() */
61 for (--linesize, i = 0; i < linesize; i++)
62 if (line[i] == '\n')
63 break;
64 i = (i < linesize) ? i + 1 : linesize;
65 NYD2_LEAVE;
66 return i;
69 static char *
70 _fgetline_byone(char **line, size_t *linesize, size_t *llen, FILE *fp,
71 int appendnl, size_t n n_MEMORY_DEBUG_ARGS)
73 char *rv;
74 int c;
75 NYD2_ENTER;
77 assert(*linesize == 0 || *line != NULL);
78 n_pstate &= ~n_PS_READLINE_NL;
80 for (rv = *line;;) {
81 if (*linesize <= LINESIZE || n >= *linesize - 128) {
82 *linesize += ((rv == NULL) ? LINESIZE + n + 1 : 256);
83 *line = rv = (n_realloc)(rv, *linesize n_MEMORY_DEBUG_ARGSCALL);
85 c = getc(fp);
86 if (c != EOF) {
87 rv[n++] = c;
88 rv[n] = '\0';
89 if (c == '\n') {
90 n_pstate |= n_PS_READLINE_NL;
91 break;
93 } else {
94 if (n > 0) {
95 if (appendnl) {
96 rv[n++] = '\n';
97 rv[n] = '\0';
99 break;
100 } else {
101 rv = NULL;
102 goto jleave;
106 if (llen)
107 *llen = n;
108 jleave:
109 NYD2_LEAVE;
110 return rv;
113 static bool_t
114 a_file_lock(int fd, enum n_file_lock_type flt, off_t off, off_t len)
116 struct flock flp;
117 bool_t rv;
118 NYD2_ENTER;
120 memset(&flp, 0, sizeof flp);
122 switch (flt) {
123 default:
124 case FLT_READ: rv = F_RDLCK; break;
125 case FLT_WRITE: rv = F_WRLCK; break;
127 flp.l_type = rv;
128 flp.l_start = off;
129 flp.l_whence = SEEK_SET;
130 flp.l_len = len;
132 if (!(rv = (fcntl(fd, F_SETLK, &flp) != -1)))
133 switch (n_err_no) {
134 case n_ERR_BADF:
135 case n_ERR_INVAL:
136 rv = TRUM1;
137 break;
139 NYD2_LEAVE;
140 return rv;
143 FL char *
144 (fgetline)(char **line, size_t *linesize, size_t *cnt, size_t *llen, FILE *fp,
145 int appendnl n_MEMORY_DEBUG_ARGS)
147 size_t i_llen, sz;
148 char *rv;
149 NYD2_ENTER;
151 if (cnt == NULL) {
152 /* Without count, we can't determine where the chars returned by fgets()
153 * end if there's no newline. We have to read one character by one */
154 rv = _fgetline_byone(line, linesize, llen, fp, appendnl, 0
155 n_MEMORY_DEBUG_ARGSCALL);
156 goto jleave;
159 n_pstate &= ~n_PS_READLINE_NL;
161 if ((rv = *line) == NULL || *linesize < LINESIZE)
162 *line = rv = (n_realloc)(rv, *linesize = LINESIZE
163 n_MEMORY_DEBUG_ARGSCALL);
164 sz = (*linesize <= *cnt) ? *linesize : *cnt + 1;
165 if (sz <= 1 || fgets(rv, sz, fp) == NULL) {
166 /* Leave llen untouched; it is used to determine whether the last line
167 * was \n-terminated in some callers */
168 rv = NULL;
169 goto jleave;
172 i_llen = _length_of_line(rv, sz);
173 *cnt -= i_llen;
174 while (rv[i_llen - 1] != '\n') {
175 *line = rv = (n_realloc)(rv, *linesize += 256 n_MEMORY_DEBUG_ARGSCALL);
176 sz = *linesize - i_llen;
177 sz = (sz <= *cnt) ? sz : *cnt + 1;
178 if (sz <= 1 || fgets(rv + i_llen, sz, fp) == NULL) {
179 if (appendnl) {
180 rv[i_llen++] = '\n';
181 rv[i_llen] = '\0';
183 break;
185 sz = _length_of_line(rv + i_llen, sz);
186 i_llen += sz;
187 *cnt -= sz;
189 if (llen)
190 *llen = i_llen;
191 jleave:
192 NYD2_LEAVE;
193 return rv;
196 FL int
197 (readline_restart)(FILE *ibuf, char **linebuf, size_t *linesize, size_t n
198 n_MEMORY_DEBUG_ARGS)
200 /* TODO readline_restart(): always *appends* LF just to strip it again;
201 * TODO should be configurable just as for fgetline(); ..or whatever..
202 * TODO intwrap */
203 int rv = -1;
204 long sz;
205 NYD2_ENTER;
207 clearerr(ibuf);
209 /* Interrupts will cause trouble if we are inside a stdio call. As this is
210 * only relevant if input is from tty, bypass it by read(), then */
211 if ((n_psonce & n_PSO_TTYIN) && fileno(ibuf) == 0) {
212 assert(*linesize == 0 || *linebuf != NULL);
213 n_pstate &= ~n_PS_READLINE_NL;
214 for (;;) {
215 if (*linesize <= LINESIZE || n >= *linesize - 128) {
216 *linesize += ((*linebuf == NULL) ? LINESIZE + n + 1 : 256);
217 *linebuf = (n_realloc)(*linebuf, *linesize n_MEMORY_DEBUG_ARGSCALL);
219 jagain:
220 sz = read(0, *linebuf + n, *linesize - n - 1);
221 if (sz > 0) {
222 n += sz;
223 (*linebuf)[n] = '\0';
224 if ((*linebuf)[n - 1] == '\n') {
225 n_pstate |= n_PS_READLINE_NL;
226 break;
228 } else {
229 if (sz < 0 && n_err_no == n_ERR_INTR)
230 goto jagain;
231 /* TODO eh. what is this? that now supposed to be a line?!? */
232 if (n > 0) {
233 if ((*linebuf)[n - 1] != '\n') {
234 (*linebuf)[n++] = '\n';
235 (*linebuf)[n] = '\0';
236 } else
237 n_pstate |= n_PS_READLINE_NL;
238 break;
239 } else
240 goto jleave;
243 } else {
244 /* Not reading from standard input or standard input not a terminal. We
245 * read one char at a time as it is the only way to get lines with
246 * embedded NUL characters in standard stdio */
247 if (_fgetline_byone(linebuf, linesize, &n, ibuf, 1, n
248 n_MEMORY_DEBUG_ARGSCALL) == NULL)
249 goto jleave;
251 if (n > 0 && (*linebuf)[n - 1] == '\n')
252 (*linebuf)[--n] = '\0';
253 rv = (int)n;
254 jleave:
255 NYD2_LEAVE;
256 return rv;
259 FL off_t
260 fsize(FILE *iob)
262 struct stat sbuf;
263 off_t rv;
264 NYD_ENTER;
266 rv = (fstat(fileno(iob), &sbuf) == -1) ? 0 : sbuf.st_size;
267 NYD_LEAVE;
268 return rv;
271 FL bool_t
272 n_file_lock(int fd, enum n_file_lock_type flt, off_t off, off_t len,
273 size_t pollmsecs)
275 size_t tries;
276 bool_t didmsg, rv;
277 NYD_ENTER;
279 if(pollmsecs == UIZ_MAX)
280 pollmsecs = FILE_LOCK_MILLIS;
282 n_UNINIT(rv, 0);
283 for (didmsg = FAL0, tries = 0; tries <= FILE_LOCK_TRIES; ++tries) {
284 rv = a_file_lock(fd, flt, off, len);
286 if (rv == TRUM1) {
287 rv = FAL0;
288 break;
290 if (rv || pollmsecs == 0)
291 break;
292 else {
293 if(!didmsg){
294 n_err(_("Failed to create a file lock, waiting %lu milliseconds "),
295 pollmsecs);
296 didmsg = TRU1;
297 }else
298 n_err(".");
299 n_msleep(pollmsecs, FAL0);
302 if(didmsg)
303 n_err(" %s\n", (rv ? _("ok") : _("failure")));
304 NYD_LEAVE;
305 return rv;
308 /* s-it-mode */