(BWDIC!) Allow `source' in `call'ed macros..
[s-mailx.git] / fio.c
blob85ec47f8988db30c4ce12a96f1f5ca267b89881d
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 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
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 SMALLOC_DEBUG_ARGS);
50 /* Workhorse */
51 static bool_t a_file_lock(int fd, enum n_file_lock_type ft, off_t off, off_t len);
53 static size_t
54 _length_of_line(char const *line, size_t linesize)
56 size_t i;
57 NYD2_ENTER;
59 /* Last character is always '\0' and was added by fgets() */
60 for (--linesize, i = 0; i < linesize; i++)
61 if (line[i] == '\n')
62 break;
63 i = (i < linesize) ? i + 1 : linesize;
64 NYD2_LEAVE;
65 return i;
68 static char *
69 _fgetline_byone(char **line, size_t *linesize, size_t *llen, FILE *fp,
70 int appendnl, size_t n SMALLOC_DEBUG_ARGS)
72 char *rv;
73 int c;
74 NYD2_ENTER;
76 assert(*linesize == 0 || *line != NULL);
77 for (rv = *line;;) {
78 if (*linesize <= LINESIZE || n >= *linesize - 128) {
79 *linesize += ((rv == NULL) ? LINESIZE + n + 1 : 256);
80 *line = rv = (srealloc)(rv, *linesize SMALLOC_DEBUG_ARGSCALL);
82 c = getc(fp);
83 if (c != EOF) {
84 rv[n++] = c;
85 rv[n] = '\0';
86 if (c == '\n')
87 break;
88 } else {
89 if (n > 0) {
90 if (appendnl) {
91 rv[n++] = '\n';
92 rv[n] = '\0';
94 break;
95 } else {
96 rv = NULL;
97 goto jleave;
101 if (llen)
102 *llen = n;
103 jleave:
104 NYD2_LEAVE;
105 return rv;
108 static bool_t
109 a_file_lock(int fd, enum n_file_lock_type flt, off_t off, off_t len)
111 struct flock flp;
112 bool_t rv;
113 NYD2_ENTER;
115 memset(&flp, 0, sizeof flp);
117 switch (flt) {
118 default:
119 case FLT_READ: rv = F_RDLCK; break;
120 case FLT_WRITE: rv = F_WRLCK; break;
122 flp.l_type = rv;
123 flp.l_start = off;
124 flp.l_whence = SEEK_SET;
125 flp.l_len = len;
127 rv = (fcntl(fd, F_SETLK, &flp) != -1);
128 NYD2_LEAVE;
129 return rv;
132 FL char *
133 (fgetline)(char **line, size_t *linesize, size_t *cnt, size_t *llen, FILE *fp,
134 int appendnl SMALLOC_DEBUG_ARGS)
136 size_t i_llen, sz;
137 char *rv;
138 NYD2_ENTER;
140 if (cnt == NULL) {
141 /* Without count, we can't determine where the chars returned by fgets()
142 * end if there's no newline. We have to read one character by one */
143 rv = _fgetline_byone(line, linesize, llen, fp, appendnl, 0
144 SMALLOC_DEBUG_ARGSCALL);
145 goto jleave;
148 if ((rv = *line) == NULL || *linesize < LINESIZE)
149 *line = rv = (srealloc)(rv, *linesize = LINESIZE SMALLOC_DEBUG_ARGSCALL);
150 sz = (*linesize <= *cnt) ? *linesize : *cnt + 1;
151 if (sz <= 1 || fgets(rv, sz, fp) == NULL) {
152 /* Leave llen untouched; it is used to determine whether the last line
153 * was \n-terminated in some callers */
154 rv = NULL;
155 goto jleave;
158 i_llen = _length_of_line(rv, sz);
159 *cnt -= i_llen;
160 while (rv[i_llen - 1] != '\n') {
161 *line = rv = (srealloc)(rv, *linesize += 256 SMALLOC_DEBUG_ARGSCALL);
162 sz = *linesize - i_llen;
163 sz = (sz <= *cnt) ? sz : *cnt + 1;
164 if (sz <= 1 || fgets(rv + i_llen, sz, fp) == NULL) {
165 if (appendnl) {
166 rv[i_llen++] = '\n';
167 rv[i_llen] = '\0';
169 break;
171 sz = _length_of_line(rv + i_llen, sz);
172 i_llen += sz;
173 *cnt -= sz;
175 if (llen)
176 *llen = i_llen;
177 jleave:
178 NYD2_LEAVE;
179 return rv;
182 FL int
183 (readline_restart)(FILE *ibuf, char **linebuf, size_t *linesize, size_t n
184 SMALLOC_DEBUG_ARGS)
186 /* TODO readline_restart(): always *appends* LF just to strip it again;
187 * TODO should be configurable just as for fgetline(); ..or whatever.. */
188 int rv = -1;
189 long sz;
190 NYD2_ENTER;
192 clearerr(ibuf);
194 /* Interrupts will cause trouble if we are inside a stdio call. As this is
195 * only relevant if input is from tty, bypass it by read(), then */
196 if (fileno(ibuf) == 0 && (options & OPT_TTYIN)) {
197 assert(*linesize == 0 || *linebuf != NULL);
198 for (;;) {
199 if (*linesize <= LINESIZE || n >= *linesize - 128) {
200 *linesize += ((*linebuf == NULL) ? LINESIZE + n + 1 : 256);
201 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
203 jagain:
204 sz = read(0, *linebuf + n, *linesize - n - 1);
205 if (sz > 0) {
206 n += sz;
207 (*linebuf)[n] = '\0';
208 if (n > 0 && (*linebuf)[n - 1] == '\n')
209 break;
210 } else {
211 if (sz < 0 && errno == EINTR)
212 goto jagain;
213 if (n > 0) {
214 if ((*linebuf)[n - 1] != '\n') {
215 (*linebuf)[n++] = '\n';
216 (*linebuf)[n] = '\0';
218 break;
219 } else
220 goto jleave;
223 } else {
224 /* Not reading from standard input or standard input not a terminal. We
225 * read one char at a time as it is the only way to get lines with
226 * embedded NUL characters in standard stdio */
227 if (_fgetline_byone(linebuf, linesize, &n, ibuf, 1, n
228 SMALLOC_DEBUG_ARGSCALL) == NULL)
229 goto jleave;
231 if (n > 0 && (*linebuf)[n - 1] == '\n')
232 (*linebuf)[--n] = '\0';
233 rv = (int)n;
234 jleave:
235 NYD2_LEAVE;
236 return rv;
239 FL void
240 setptr(FILE *ibuf, off_t offset)
242 struct message self;
243 char *cp, *linebuf = NULL;
244 char const *cp2;
245 int c, maybe = 1, inhead = 0, selfcnt = 0;
246 size_t linesize = 0, filesize, cnt;
247 NYD_ENTER;
249 memset(&self, 0, sizeof self);
250 self.m_flag = MUSED | MNEW | MNEWEST;
251 filesize = mailsize - offset;
252 offset = ftell(mb.mb_otf);
254 for (;;) {
255 if (fgetline(&linebuf, &linesize, &filesize, &cnt, ibuf, 0) == NULL) {
256 self.m_xsize = self.m_size;
257 self.m_xlines = self.m_lines;
258 self.m_have = HAVE_HEADER | HAVE_BODY;
259 if (selfcnt > 0)
260 message_append(&self);
261 message_append_null();
262 if (linebuf != NULL)
263 free(linebuf);
264 break;
267 #ifdef notdef
268 if (linebuf[0] == '\0')
269 linebuf[0] = '.';
270 #endif
271 /* XXX Convert CRLF to LF; this should be rethought in that
272 * XXX CRLF input should possibly end as CRLF output? */
273 if (cnt >= 2 && linebuf[cnt - 1] == '\n' && linebuf[cnt - 2] == '\r')
274 linebuf[--cnt - 1] = '\n';
275 fwrite(linebuf, sizeof *linebuf, cnt, mb.mb_otf);
276 if (ferror(mb.mb_otf)) {
277 n_perr(_("/tmp"), 0);
278 exit(EXIT_ERR);
280 if (linebuf[cnt - 1] == '\n')
281 linebuf[cnt - 1] = '\0';
282 if (maybe && linebuf[0] == 'F' && is_head(linebuf, cnt, FAL0)) {
283 /* TODO char date[FROM_DATEBUF];
284 * TODO extract_date_from_from_(linebuf, cnt, date);
285 * TODO self.m_time = 10000; */
286 self.m_xsize = self.m_size;
287 self.m_xlines = self.m_lines;
288 self.m_have = HAVE_HEADER | HAVE_BODY;
289 if (selfcnt++ > 0)
290 message_append(&self);
291 msgCount++;
292 self.m_flag = MUSED | MNEW | MNEWEST;
293 self.m_size = 0;
294 self.m_lines = 0;
295 self.m_block = mailx_blockof(offset);
296 self.m_offset = mailx_offsetof(offset);
297 inhead = 1;
298 } else if (linebuf[0] == 0) {
299 inhead = 0;
300 } else if (inhead) {
301 for (cp = linebuf, cp2 = "status";; ++cp) {
302 if ((c = *cp2++) == 0) {
303 while (c = *cp++, whitechar(c))
305 if (cp[-1] != ':')
306 break;
307 while ((c = *cp++) != '\0')
308 if (c == 'R')
309 self.m_flag |= MREAD;
310 else if (c == 'O')
311 self.m_flag &= ~MNEW;
312 break;
314 if (*cp != c && *cp != upperconv(c))
315 break;
317 for (cp = linebuf, cp2 = "x-status";; ++cp) {
318 if ((c = *cp2++) == 0) {
319 while ((c = *cp++, whitechar(c)))
321 if (cp[-1] != ':')
322 break;
323 while ((c = *cp++) != '\0')
324 if (c == 'F')
325 self.m_flag |= MFLAGGED;
326 else if (c == 'A')
327 self.m_flag |= MANSWERED;
328 else if (c == 'T')
329 self.m_flag |= MDRAFTED;
330 break;
332 if (*cp != c && *cp != upperconv(c))
333 break;
336 offset += cnt;
337 self.m_size += cnt;
338 ++self.m_lines;
339 maybe = linebuf[0] == 0;
341 NYD_LEAVE;
344 FL int
345 putline(FILE *obuf, char *linebuf, size_t cnt)
347 int rv = -1;
348 NYD_ENTER;
350 fwrite(linebuf, sizeof *linebuf, cnt, obuf);
351 putc('\n', obuf);
352 if (!ferror(obuf))
353 rv = (int)(cnt + 1);
354 NYD_LEAVE;
355 return rv;
358 FL off_t
359 fsize(FILE *iob)
361 struct stat sbuf;
362 off_t rv;
363 NYD_ENTER;
365 rv = (fstat(fileno(iob), &sbuf) == -1) ? 0 : sbuf.st_size;
366 NYD_LEAVE;
367 return rv;
370 FL bool_t
371 var_folder_updated(char const *name, char **store)
373 char rv = TRU1;
374 char *folder, *unres = NULL, *res = NULL;
375 NYD_ENTER;
377 if ((folder = UNCONST(name)) == NULL)
378 goto jleave;
380 /* Expand the *folder*; skip %: prefix for simplicity of use */
381 /* XXX This *only* works because we do NOT
382 * XXX update environment variables via the "set" mechanism */
383 if (folder[0] == '%' && folder[1] == ':')
384 folder += 2;
385 if ((folder = fexpand(folder, FEXP_FULL)) == NULL) /* XXX error? */
386 goto jleave;
388 switch (which_protocol(folder)) {
389 case PROTO_POP3:
390 n_err(_("*folder* cannot be set to a flat, readonly POP3 account\n"));
391 rv = FAL0;
392 goto jleave;
393 default:
394 /* Further expansion desired */
395 break;
398 /* All non-absolute paths are relative to our home directory */
399 if (*folder != '/') {
400 size_t l1 = strlen(homedir), l2 = strlen(folder);
401 unres = ac_alloc(l1 + l2 + 1 +1);
402 memcpy(unres, homedir, l1);
403 unres[l1] = '/';
404 memcpy(unres + l1 + 1, folder, l2);
405 unres[l1 + 1 + l2] = '\0';
406 folder = unres;
409 /* Since lex.c:_update_mailname() uses realpath(3) if available to
410 * avoid that we loose track of our currently open folder in case we
411 * chdir away, but still checks the leading path portion against
412 * getfold() to be able to abbreviate to the +FOLDER syntax if
413 * possible, we need to realpath(3) the folder, too */
414 #ifdef HAVE_REALPATH
415 res = ac_alloc(PATH_MAX +1);
416 if (realpath(folder, res) == NULL)
417 n_err(_("Can't canonicalize \"%s\"\n"), folder);
418 else
419 folder = res;
420 #endif
422 *store = sstrdup(folder);
424 if (res != NULL)
425 ac_free(res);
426 if (unres != NULL)
427 ac_free(unres);
428 jleave:
429 NYD_LEAVE;
430 return rv;
433 FL char const *
434 getdeadletter(void) /* XXX should that be in auxlily.c? */
436 char const *cp;
437 NYD_ENTER;
439 if ((cp = ok_vlook(DEAD)) == NULL || (cp = fexpand(cp, FEXP_LOCAL)) == NULL)
440 cp = fexpand("~/dead.letter", FEXP_LOCAL | FEXP_SHELL);
441 else if (*cp != '/') {
442 size_t sz = strlen(cp) + 2 +1;
443 char *buf = ac_alloc(sz);
445 snprintf(buf, sz, "~/%s", cp);
446 cp = fexpand(buf, FEXP_LOCAL | FEXP_SHELL);
447 ac_free(buf);
450 if (cp == NULL)
451 cp = "dead.letter"; /* XXX magic -> nail.h (POSIX thing though) */
452 NYD_LEAVE;
453 return cp;
456 FL bool_t
457 n_file_lock(int fd, enum n_file_lock_type flt, off_t off, off_t len,
458 size_t pollmsecs)
460 size_t tries;
461 bool_t rv;
462 NYD_ENTER;
464 for (tries = 0; tries <= FILE_LOCK_TRIES; ++tries)
465 if ((rv = a_file_lock(fd, flt, off, len)) || pollmsecs == 0)
466 break;
467 else
468 n_msleep(pollmsecs, FAL0);
469 NYD_LEAVE;
470 return rv;
473 /* s-it-mode */