Merge branch 'topic/coverity-444-v14.9.11-1'
[s-mailx.git] / fio.c
blob4eb910fe9fa4c15eaeeac84daa05e9d57d57c723
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 * SPDX-License-Identifier: BSD-3-Clause
7 */
8 /*
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
36 #undef n_FILE
37 #define n_FILE fio
39 #ifndef HAVE_AMALGAMATION
40 # include "nail.h"
41 #endif
43 /* line is a buffer with the result of fgets(). Returns the first newline or
44 * the last character read */
45 static size_t _length_of_line(char const *line, size_t linesize);
47 /* Read a line, one character at a time */
48 static char * _fgetline_byone(char **line, size_t *linesize, size_t *llen,
49 FILE *fp, int appendnl, size_t n n_MEMORY_DEBUG_ARGS);
51 /* Workhorse */
52 static bool_t a_file_lock(int fd, enum n_file_lock_type ft, off_t off,
53 off_t len);
55 static size_t
56 _length_of_line(char const *line, size_t linesize)
58 size_t i;
59 NYD2_ENTER;
61 /* Last character is always '\0' and was added by fgets() */
62 for (--linesize, i = 0; i < linesize; i++)
63 if (line[i] == '\n')
64 break;
65 i = (i < linesize) ? i + 1 : linesize;
66 NYD2_LEAVE;
67 return i;
70 static char *
71 _fgetline_byone(char **line, size_t *linesize, size_t *llen, FILE *fp,
72 int appendnl, size_t n n_MEMORY_DEBUG_ARGS)
74 char *rv;
75 int c;
76 NYD2_ENTER;
78 assert(*linesize == 0 || *line != NULL);
79 n_pstate &= ~n_PS_READLINE_NL;
81 for (rv = *line;;) {
82 if (*linesize <= LINESIZE || n >= *linesize - 128) {
83 *linesize += ((rv == NULL) ? LINESIZE + n + 1 : 256);
84 *line = rv = (n_realloc)(rv, *linesize n_MEMORY_DEBUG_ARGSCALL);
86 c = getc(fp);
87 if (c != EOF) {
88 rv[n++] = c;
89 rv[n] = '\0';
90 if (c == '\n') {
91 n_pstate |= n_PS_READLINE_NL;
92 break;
94 } else {
95 if (n > 0) {
96 if (appendnl) {
97 rv[n++] = '\n';
98 rv[n] = '\0';
100 break;
101 } else {
102 rv = NULL;
103 goto jleave;
107 if (llen)
108 *llen = n;
109 jleave:
110 NYD2_LEAVE;
111 return rv;
114 static bool_t
115 a_file_lock(int fd, enum n_file_lock_type flt, off_t off, off_t len)
117 struct flock flp;
118 bool_t rv;
119 NYD2_ENTER;
121 memset(&flp, 0, sizeof flp);
123 switch (flt) {
124 default:
125 case FLT_READ: rv = F_RDLCK; break;
126 case FLT_WRITE: rv = F_WRLCK; break;
128 flp.l_type = rv;
129 flp.l_start = off;
130 flp.l_whence = SEEK_SET;
131 flp.l_len = len;
133 if (!(rv = (fcntl(fd, F_SETLK, &flp) != -1)))
134 switch (n_err_no) {
135 case n_ERR_BADF:
136 case n_ERR_INVAL:
137 rv = TRUM1;
138 break;
140 NYD2_LEAVE;
141 return rv;
144 FL char *
145 (fgetline)(char **line, size_t *linesize, size_t *cnt, size_t *llen, FILE *fp,
146 int appendnl n_MEMORY_DEBUG_ARGS)
148 size_t i_llen, sz;
149 char *rv;
150 NYD2_ENTER;
152 if (cnt == NULL) {
153 /* Without count, we can't determine where the chars returned by fgets()
154 * end if there's no newline. We have to read one character by one */
155 rv = _fgetline_byone(line, linesize, llen, fp, appendnl, 0
156 n_MEMORY_DEBUG_ARGSCALL);
157 goto jleave;
160 n_pstate &= ~n_PS_READLINE_NL;
162 if ((rv = *line) == NULL || *linesize < LINESIZE)
163 *line = rv = (n_realloc)(rv, *linesize = LINESIZE
164 n_MEMORY_DEBUG_ARGSCALL);
165 sz = (*linesize <= *cnt) ? *linesize : *cnt + 1;
166 if (sz <= 1 || fgets(rv, sz, fp) == NULL) {
167 /* Leave llen untouched; it is used to determine whether the last line
168 * was \n-terminated in some callers */
169 rv = NULL;
170 goto jleave;
173 i_llen = _length_of_line(rv, sz);
174 *cnt -= i_llen;
175 while (rv[i_llen - 1] != '\n') {
176 *line = rv = (n_realloc)(rv, *linesize += 256 n_MEMORY_DEBUG_ARGSCALL);
177 sz = *linesize - i_llen;
178 sz = (sz <= *cnt) ? sz : *cnt + 1;
179 if (sz <= 1 || fgets(rv + i_llen, sz, fp) == NULL) {
180 if (appendnl) {
181 rv[i_llen++] = '\n';
182 rv[i_llen] = '\0';
184 break;
186 sz = _length_of_line(rv + i_llen, sz);
187 i_llen += sz;
188 *cnt -= sz;
190 if (llen)
191 *llen = i_llen;
192 jleave:
193 NYD2_LEAVE;
194 return rv;
197 FL int
198 (readline_restart)(FILE *ibuf, char **linebuf, size_t *linesize, size_t n
199 n_MEMORY_DEBUG_ARGS)
201 /* TODO readline_restart(): always *appends* LF just to strip it again;
202 * TODO should be configurable just as for fgetline(); ..or whatever..
203 * TODO intwrap */
204 int rv = -1;
205 long sz;
206 NYD2_ENTER;
208 clearerr(ibuf);
210 /* Interrupts will cause trouble if we are inside a stdio call. As this is
211 * only relevant if input is from tty, bypass it by read(), then */
212 if ((n_psonce & n_PSO_TTYIN) && fileno(ibuf) == 0) {
213 assert(*linesize == 0 || *linebuf != NULL);
214 n_pstate &= ~n_PS_READLINE_NL;
215 for (;;) {
216 if (*linesize <= LINESIZE || n >= *linesize - 128) {
217 *linesize += ((*linebuf == NULL) ? LINESIZE + n + 1 : 256);
218 *linebuf = (n_realloc)(*linebuf, *linesize n_MEMORY_DEBUG_ARGSCALL);
220 jagain:
221 sz = read(0, *linebuf + n, *linesize - n - 1);
222 if (sz > 0) {
223 n += sz;
224 (*linebuf)[n] = '\0';
225 if ((*linebuf)[n - 1] == '\n') {
226 n_pstate |= n_PS_READLINE_NL;
227 break;
229 } else {
230 if (sz < 0 && n_err_no == n_ERR_INTR)
231 goto jagain;
232 /* TODO eh. what is this? that now supposed to be a line?!? */
233 if (n > 0) {
234 if ((*linebuf)[n - 1] != '\n') {
235 (*linebuf)[n++] = '\n';
236 (*linebuf)[n] = '\0';
237 } else
238 n_pstate |= n_PS_READLINE_NL;
239 break;
240 } else
241 goto jleave;
244 } else {
245 /* Not reading from standard input or standard input not a terminal. We
246 * read one char at a time as it is the only way to get lines with
247 * embedded NUL characters in standard stdio */
248 if (_fgetline_byone(linebuf, linesize, &n, ibuf, 1, n
249 n_MEMORY_DEBUG_ARGSCALL) == NULL)
250 goto jleave;
252 if (n > 0 && (*linebuf)[n - 1] == '\n')
253 (*linebuf)[--n] = '\0';
254 rv = (int)n;
255 jleave:
256 NYD2_LEAVE;
257 return rv;
260 FL off_t
261 fsize(FILE *iob)
263 struct stat sbuf;
264 off_t rv;
265 NYD_ENTER;
267 rv = (fstat(fileno(iob), &sbuf) == -1) ? 0 : sbuf.st_size;
268 NYD_LEAVE;
269 return rv;
272 FL bool_t
273 n_file_lock(int fd, enum n_file_lock_type flt, off_t off, off_t len,
274 size_t pollmsecs)
276 size_t tries;
277 bool_t didmsg, rv;
278 NYD_ENTER;
280 if(pollmsecs == UIZ_MAX)
281 pollmsecs = FILE_LOCK_MILLIS;
283 n_UNINIT(rv, 0);
284 for (didmsg = FAL0, tries = 0; tries <= FILE_LOCK_TRIES; ++tries) {
285 rv = a_file_lock(fd, flt, off, len);
287 if (rv == TRUM1) {
288 rv = FAL0;
289 break;
291 if (rv || pollmsecs == 0)
292 break;
293 else {
294 if(!didmsg){
295 n_err(_("Failed to create a file lock, waiting %lu milliseconds "),
296 pollmsecs);
297 didmsg = TRU1;
298 }else
299 n_err(".");
300 n_msleep(pollmsecs, FAL0);
303 if(didmsg)
304 n_err(" %s\n", (rv ? _("ok") : _("failure")));
305 NYD_LEAVE;
306 return rv;
309 /* s-it-mode */