1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Implementation of file-streams.h.
3 *@ TODO tmp_release() should be removed: tmp_open() should take an optional
4 *@ TODO vector of NIL terminated {char const *mode;sz fd_result;} structs,
5 *@ TODO and create all desired copies; drop HOLDSIGS, then, too!
6 *
7 * Copyright (c) 2012 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
8 * SPDX-License-Identifier: ISC
9 *
10 * Permission to use, copy, modify, and/or distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22 #undef su_FILE
23 #define su_FILE file_streams
24 #define mx_SOURCE
25 #define mx_SOURCE_FILE_STREAMS
26
27 #ifndef mx_HAVE_AMALGAMATION
28 # include "mx/nail.h"
29 #endif
30
31 #include <fcntl.h>
32
33 #include <su/cs.h>
34 #include <su/mem.h>
35
36 #include "mx/child.h"
37 #include "mx/cmd-filetype.h"
38 #include "mx/random.h"
39 #include "mx/sigs.h"
40 #include "mx/termcap.h"
41
42 #include "mx/file-streams.h"
43 #include "su/code-in.h"
44
45 #ifdef O_CLOEXEC
46 # define a_FS__O_CLOEXEC O_CLOEXEC
47 #else
48 # define a_FS__O_CLOEXEC 0
49 #endif
50
51 enum{
52 a_FS_PIPE_READ = 0,
53 a_FS_PIPE_WRITE = 1
54 };
55
56 enum a_fs_ent_flags{
57 a_FS_EF_RAW,
58 a_FS_EF_IMAP = 1u<<1,
59 a_FS_EF_MAILDIR = 1u<<2,
60 a_FS_EF_HOOK = 1u<<3,
61 a_FS_EF_PIPE = 1u<<4,
62 a_FS_EF_MASK = (1u<<7) - 1,
63 /* TODO a_FS_EF_UNLINK: should be in a separate process so that unlinking
64 * TODO the temporary "garbage" is "safe"(r than it is like that) */
65 a_FS_EF_UNLINK = 1u<<8,
66 /* mx_fs_tmp_release() callable? */
67 a_FS_EF_HOLDSIGS = 1u<<9,
68 a_FS_EF_FD_PASS_NEEDS_WAIT = 1u<<10
69 };
70
71 /* This struct is what backs struct mx_fs_tmp_ctx! */
72 struct a_fs_ent{
73 char *fse_realfile;
74 struct a_fs_ent *fse_link;
75 u32 fse_flags;
76 int fse_omode;
77 long fse_offset; /* TODO SU I/O 64-bit */
78 FILE *fse_fp;
79 char *fse_save_cmd;
80 struct mx_child_ctx fse_cc;
81 };
82
83 struct a_fs_lpool_ent{
84 struct a_fs_lpool_ent *fsle_last;
85 char *fsle_dat;
86 uz fsle_size;
87 };
88
89 static struct a_fs_ent *a_fs_fp_head;
90
91 struct a_fs_lpool_ent *a_fs_lpool_free;
92 struct a_fs_lpool_ent *a_fs_lpool_used;
93
94 /* Scan file open mode, and turn it to flags. If & was prepended, set *mode
95 * to NIL to indicate O_REGISTER shall not be set */
96 static boole a_fs_scan_mode(char const **mode, int *omode);
97
98 /* */
99 static struct a_fs_ent *a_fs_register_file(FILE *fp, int omode,
100 struct mx_child_ctx *ccp,
101 u32 flags, char const *realfile, long offset, char const *save_cmd);
102 static boole a_fs_unregister_file(FILE *fp);
103
104 /* */
105 static boole a_fs_file_load(uz flags, int infd, int outfd,
106 char const *load_cmd);
107 static boole a_fs_file_save(struct a_fs_ent *fpp);
108
109 static boole
a_fs_scan_mode(char const ** mode,int * omode)110 a_fs_scan_mode(char const **mode, int *omode){
111 static struct{
112 char const mode[4];
113 int omode;
114 }const maps[] = {
115 {"r", O_RDONLY},
116 {"w", O_WRONLY | O_CREAT | mx_O_NOXY_BITS | O_TRUNC},
117 {"wx", O_WRONLY | O_CREAT | O_EXCL},
118 {"a", O_WRONLY | O_APPEND | O_CREAT | mx_O_NOXY_BITS},
119 {"a+", O_RDWR | O_APPEND | O_CREAT | mx_O_NOXY_BITS},
120 {"r+", O_RDWR},
121 {"w+", O_RDWR | O_CREAT | mx_O_NOXY_BITS | O_TRUNC},
122 {"W+", O_RDWR | O_CREAT | O_EXCL}
123 };
124 boole rv;
125 uz i;
126 char const *xmode;
127 NYD2_IN;
128
129 if((xmode = *mode)[0] == '&'){
130 *mode = NIL;
131 ++xmode;
132 }
133
134 for(i = 0; i < NELEM(maps); ++i)
135 if(!su_cs_cmp(maps[i].mode, xmode)){
136 *omode = maps[i].omode;
137 rv = TRU1;
138 goto jleave;
139 }
140
141 su_DBG( n_alert(_("Internal error: bad stdio open mode %s"), xmode); )
142 su_NDBG( su_err_set_no(su_ERR_INVAL); )
143 *omode = 0; /* (silence CC) */
144
145 rv = FAL0;
146 jleave:
147 NYD2_OU;
148 return rv;
149 }
150
151 static struct a_fs_ent *
a_fs_register_file(FILE * fp,int omode,struct mx_child_ctx * ccp,u32 flags,char const * realfile,long offset,char const * save_cmd)152 a_fs_register_file(FILE *fp, int omode, struct mx_child_ctx *ccp, u32 flags,
153 char const *realfile, long offset, char const *save_cmd){
154 struct a_fs_ent *fsep;
155 NYD_IN;
156
157 ASSERT(!(flags & a_FS_EF_UNLINK) || realfile != NIL);
158
159 fsep = su_TCALLOC(struct a_fs_ent, 1);
160 if(realfile != NIL)
161 fsep->fse_realfile = su_cs_dup(realfile, 0);
162 fsep->fse_link = a_fs_fp_head;
163 fsep->fse_flags = flags;
164 fsep->fse_omode = omode;
165 fsep->fse_offset = offset;
166 fsep->fse_fp = fp;
167 if(save_cmd != NIL)
168 fsep->fse_save_cmd = su_cs_dup(save_cmd, 0);
169 if(ccp != NIL)
170 fsep->fse_cc = *ccp;
171
172 a_fs_fp_head = fsep;
173
174 NYD_OU;
175 return fsep;
176 }
177
178 static boole
a_fs_unregister_file(FILE * fp)179 a_fs_unregister_file(FILE *fp){
180 struct a_fs_ent **fsepp, *fsep;
181 boole rv;
182 NYD_IN;
183
184 rv = TRU1;
185
186 for(fsepp = &a_fs_fp_head;; fsepp = &fsep->fse_link){
187 if(UNLIKELY((fsep = *fsepp) == NIL)){
188 su_DBGOR(n_panic, n_alert)(_("Invalid file pointer"));
189 rv = FAL0;
190 break;
191 }else if(fsep->fse_fp != fp)
192 continue;
193
194 switch(fsep->fse_flags & a_FS_EF_MASK){
195 case a_FS_EF_RAW:
196 case a_FS_EF_PIPE:
197 break;
198 default:
199 rv = a_fs_file_save(fsep);
200 break;
201 }
202
203 if((fsep->fse_flags & a_FS_EF_UNLINK) && unlink(fsep->fse_realfile))
204 rv = FAL0;
205
206 *fsepp = fsep->fse_link;
207 if(fsep->fse_realfile != NIL)
208 su_FREE(fsep->fse_realfile);
209 if(fsep->fse_save_cmd != NIL)
210 su_FREE(fsep->fse_save_cmd);
211
212 su_FREE(fsep);
213 break;
214 }
215
216 NYD_OU;
217 return rv;
218 }
219
220 static boole
a_fs_file_load(uz flags,int infd,int outfd,char const * load_cmd)221 a_fs_file_load(uz flags, int infd, int outfd, char const *load_cmd){
222 struct mx_child_ctx cc;
223 boole rv;
224 NYD2_IN;
225
226 mx_child_ctx_setup(&cc);
227 cc.cc_flags = mx_CHILD_RUN_WAIT_LIFE;
228 cc.cc_fds[mx_CHILD_FD_IN] = infd;
229 cc.cc_fds[mx_CHILD_FD_OUT] = outfd;
230
231 switch(flags & a_FS_EF_MASK){
232 case a_FS_EF_IMAP:
233 case a_FS_EF_MAILDIR:
234 rv = TRU1;
235 break;
236 case a_FS_EF_HOOK:
237 cc.cc_cmd = ok_vlook(SHELL);
238 cc.cc_args[0] = "-c";
239 cc.cc_args[1] = load_cmd;
240 if(0){
241 /* FALLTHRU */
242 default:
243 cc.cc_cmd = mx_FS_FILETYPE_CAT_PROG;
244 }
245
246 rv = (mx_child_run(&cc) && cc.cc_exit_status == 0);
247 break;
248 }
249
250 NYD2_OU;
251 return rv;
252 }
253
254 static boole
a_fs_file_save(struct a_fs_ent * fsep)255 a_fs_file_save(struct a_fs_ent *fsep){
256 struct mx_child_ctx cc;
257 boole rv;
258 NYD_IN;
259
260 if(fsep->fse_omode == O_RDONLY){
261 rv = TRU1;
262 goto jleave;
263 }
264 rv = FAL0;
265
266 fflush(fsep->fse_fp);
267 clearerr(fsep->fse_fp);
268
269 /* Ensure the I/O library does not optimize the fseek(3) away! */
270 if(!n_real_seek(fsep->fse_fp, fsep->fse_offset, SEEK_SET)){
271 s32 err;
272
273 err = su_err_no();
274 n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
275 n_shexp_quote_cp(fsep->fse_realfile, FAL0), su_err_doc(err));
276 goto jleave;
277 }
278
279 #ifdef mx_HAVE_MAILDIR
280 if((fsep->fse_flags & a_FS_EF_MASK) == a_FS_EF_MAILDIR){
281 rv = maildir_append(fsep->fse_realfile, fsep->fse_fp, fsep->fse_offset);
282 goto jleave;
283 }
284 #endif
285
286 #ifdef mx_HAVE_IMAP
287 if((fsep->fse_flags & a_FS_EF_MASK) == a_FS_EF_IMAP){
288 rv = imap_append(fsep->fse_realfile, fsep->fse_fp, fsep->fse_offset);
289 goto jleave;
290 }
291 #endif
292
293 mx_child_ctx_setup(&cc);
294 cc.cc_flags = mx_CHILD_RUN_WAIT_LIFE;
295 cc.cc_fds[mx_CHILD_FD_IN] = fileno(fsep->fse_fp);
296 if((cc.cc_fds[mx_CHILD_FD_OUT] = open(fsep->fse_realfile,
297 ((fsep->fse_omode | O_CREAT |
298 (fsep->fse_omode & O_APPEND ? 0 : O_TRUNC) | mx_O_NOXY_BITS
299 ) & ~O_EXCL), 0666)) == -1){
300 s32 err;
301
302 err = su_err_no();
303 n_err(_("Fatal: cannot create %s: %s\n"),
304 n_shexp_quote_cp(fsep->fse_realfile, FAL0), su_err_doc(err));
305 goto jleave;
306 }
307
308 switch(fsep->fse_flags & a_FS_EF_MASK){
309 case a_FS_EF_HOOK:
310 if(n_poption & n_PO_D_V)
311 n_err(_("Using `filetype' handler %s to save %s\n"),
312 n_shexp_quote_cp(fsep->fse_save_cmd, FAL0),
313 n_shexp_quote_cp(fsep->fse_realfile, FAL0));
314 cc.cc_cmd = ok_vlook(SHELL);
315 cc.cc_args[0] = "-c";
316 cc.cc_args[1] = fsep->fse_save_cmd;
317 break;
318 default:
319 cc.cc_cmd = mx_FS_FILETYPE_CAT_PROG;
320 break;
321 }
322
323 rv = (mx_child_run(&cc) && cc.cc_exit_status == 0);
324
325 close(cc.cc_fds[mx_CHILD_FD_OUT]); /* XXX no error handling */
326 jleave:
327 NYD_OU;
328 return rv;
329 }
330
331 FILE *
mx_fs_open(char const * file,char const * oflags)332 mx_fs_open(char const *file, char const *oflags){
333 int osflags, fd;
334 char const *moflags;
335 FILE *fp;
336 NYD_IN;
337
338 fp = NIL;
339
340 moflags = oflags;
341 if(!a_fs_scan_mode(&moflags, &osflags))
342 goto jleave;
343 osflags |= a_FS__O_CLOEXEC;
344 if(moflags == NIL)
345 ++oflags;
346
347 if((fd = open(file, osflags, 0666)) == -1)
348 goto jleave;
349 if(!mx_FS_FD_CLOEXEC_SET(fd)){
350 close(fd);
351 goto jleave;
352 }
353
354 if((fp = fdopen(fd, oflags)) != NIL && moflags != NIL)
355 a_fs_register_file(fp, osflags, 0, a_FS_EF_RAW, NIL, 0L, NIL);
356
357 jleave:
358 NYD_OU;
359 return fp;
360 }
361
362 FILE *
mx_fs_open_any(char const * file,char const * oflags,enum mx_fs_open_state * fs_or_nil)363 mx_fs_open_any(char const *file, char const *oflags, /* TODO take flags */
364 enum mx_fs_open_state *fs_or_nil){ /* TODO as bits, return state */
365 /* TODO Support file locking upon open time */
366 long offset;
367 enum protocol p;
368 enum mx_fs_oflags rof;
369 uz flags;
370 int osflags, omode, infd;
371 char const *cload, *csave;
372 enum mx_fs_open_state fs;
373 s32 err;
374 FILE *rv;
375 NYD_IN;
376
377 rv = NIL;
378 err = su_ERR_NONE;
379 fs = mx_FS_OPEN_STATE_NONE;
380 cload = csave = NIL;
381
382 /* !O_REGISTER is disallowed */
383 if(!a_fs_scan_mode(&oflags, &osflags) || oflags == NIL){
384 err = su_ERR_INVAL;
385 goto jleave;
386 }
387
388 flags = 0;
389 rof = mx_FS_O_RDWR | mx_FS_O_UNLINK;
390 if(osflags & O_APPEND)
391 rof |= mx_FS_O_APPEND;
392 omode = (osflags == O_RDONLY) ? R_OK : R_OK | W_OK;
393
394 /* We don't want to find mbox.bz2 when doing "copy * mbox", but only for
395 * "file mbox", so don't try hooks when writing */
396 p = which_protocol(csave = file, TRU1, ((omode & W_OK) == 0), &file);
397 fs = S(enum mx_fs_open_state,p);
398 switch(p){
399 default:
400 err = su_ERR_OPNOTSUPP;
401 goto jleave;
402
403 case n_PROTO_IMAP:
404 #ifdef mx_HAVE_IMAP
405 file = csave;
406 flags |= a_FS_EF_IMAP;
407 osflags = O_RDWR | O_APPEND | O_CREAT | mx_O_NOXY_BITS;
408 infd = -1;
409 break;
410 #else
411 err = su_ERR_OPNOTSUPP;
412 goto jleave;
413 #endif
414
415 case n_PROTO_MAILDIR:
416 #ifdef mx_HAVE_MAILDIR
417 if(fs_or_nil != NIL && !access(file, F_OK))
418 fs |= mx_FS_OPEN_STATE_EXISTS;
419 flags |= a_FS_EF_MAILDIR;
420 osflags = O_RDWR | O_APPEND | O_CREAT | mx_O_NOXY_BITS;
421 infd = -1;
422 break;
423 #else
424 err = su_ERR_OPNOTSUPP;
425 goto jleave;
426 #endif
427
428 case n_PROTO_EML:
429 if(!(osflags & O_RDONLY)){
430 err = su_ERR_OPNOTSUPP;
431 goto jleave;
432 }
433 /* FALLTHRU */
434 case n_PROTO_FILE:{
435 struct mx_filetype ft;
436
437 if(!(osflags & O_EXCL) && fs_or_nil != NIL && !access(file, F_OK))
438 fs |= mx_FS_OPEN_STATE_EXISTS;
439
440 if(mx_filetype_exists(&ft, file)){/* TODO report real name to outside */
441 flags |= a_FS_EF_HOOK;
442 cload = ft.ft_load_dat;
443 csave = ft.ft_save_dat;
444 /* Cause truncation for compressor/hook output files */
445 osflags &= ~O_APPEND;
446 rof &= ~mx_FS_O_APPEND;
447 if((infd = open(file, (omode & W_OK ? O_RDWR : O_RDONLY))) != -1){
448 fs |= mx_FS_OPEN_STATE_EXISTS;
449 if(n_poption & n_PO_D_V)
450 n_err(_("Using `filetype' handler %s to load %s\n"),
451 n_shexp_quote_cp(cload, FAL0), n_shexp_quote_cp(file, FAL0));
452 }else{
453 err = su_err_no();
454 if(!(osflags & O_CREAT) || err != su_ERR_NOENT)
455 goto jleave;
456 }
457 }else{
458 /*flags |= a_FS_EF_RAW;*/
459 rv = mx_fs_open(file, oflags);
460 if((osflags & O_EXCL) && rv == NIL)
461 fs |= mx_FS_OPEN_STATE_EXISTS;
462 goto jleave;
463 }
464 }break;
465 }
466
467 /* Note rv is not yet register_file()d, fclose() it in error path! */
468 if((rv = mx_fs_tmp_open("fopenany", rof, NIL)) == NIL){
469 n_perr(_("tmpfile"), err = su_err_no());
470 goto Jerr;
471 }
472
473 if(flags & (a_FS_EF_IMAP | a_FS_EF_MAILDIR))
474 ;
475 else if(infd >= 0){
476 if(!a_fs_file_load(flags, infd, fileno(rv), cload)) Jerr:{
477 err = su_err_no();
478 if(rv != NIL)
479 mx_fs_close(rv);
480 rv = NIL;
481 if(infd >= 0)
482 close(infd);
483 goto jleave;
484 }
485 }else{
486 if((infd = creat(file, 0666)) == -1){
487 err = su_err_no();
488 fclose(rv);
489 rv = NIL;
490 goto jleave;
491 }
492 }
493
494 if(infd >= 0)
495 close(infd);
496 fflush(rv);
497
498 if(!(osflags & O_APPEND))
499 rewind(rv);
500
501 if((offset = ftell(rv)) == -1){
502 err = su_err_no();
503 mx_fs_close(rv);
504 rv = NIL;
505 goto jleave;
506 }
507
508 a_fs_register_file(rv, osflags, 0, flags, file, offset, csave);
509 jleave:
510 if(fs_or_nil != NIL)
511 *fs_or_nil = fs;
512 if(rv == NIL && err != su_ERR_NONE)
513 su_err_set_no(err);
514
515 NYD_OU;
516 return rv;
517 }
518
519 FILE *
mx_fs_tmp_open(char const * namehint,u32 oflags,struct mx_fs_tmp_ctx ** fstcp_or_nil)520 mx_fs_tmp_open(char const *namehint, u32 oflags,
521 struct mx_fs_tmp_ctx **fstcp_or_nil){
522 /* The 6 is arbitrary but leaves room for an eight character hint (the
523 * POSIX minimum path length is 14, though we do not check that XXX).
524 * 6 should be more than sufficient given that we use base64url encoding
525 * for our random string */
526 enum {a_RANDCHARS = 6u, a_HINT_MIN = 8u};
527
528 char *cp_base, *cp;
529 uz maxname, hintlen, i;
530 char const *tmpdir;
531 int osoflags, fd, e;
532 boole relesigs;
533 FILE *fp;
534 NYD_IN;
535
536 ASSERT(namehint != NIL);
537 ASSERT((oflags & mx_FS_O_WRONLY) || (oflags & mx_FS_O_RDWR));
538 ASSERT(!(oflags & mx_FS_O_RDONLY));
539 ASSERT(!(oflags & mx_FS_O_REGISTER_UNLINK) || (oflags & mx_FS_O_REGISTER));
540 ASSERT(!(oflags & mx_FS_O_HOLDSIGS) || !(oflags & mx_FS_O_UNLINK));
541 ASSERT(fstcp_or_nil == NIL || ((oflags & mx_FS_O_REGISTER) ||
542 (oflags & mx_FS_O_HOLDSIGS) || !(oflags & mx_FS_O_UNLINK)));
543
544 if(fstcp_or_nil != NIL)
545 *fstcp_or_nil = NIL;
546
547 fp = NIL;
548 relesigs = FAL0;
549 e = 0;
550 tmpdir = ok_vlook(TMPDIR);
551 maxname = NAME_MAX;
552
553 #ifdef mx_HAVE_PATHCONF
554 /* C99 */{
555 long pc;
556
557 if((pc = pathconf(tmpdir, _PC_NAME_MAX)) != -1){
558 maxname = S(uz,pc);
559 if(maxname < a_RANDCHARS + a_HINT_MIN){
560 su_err_set_no(su_ERR_NAMETOOLONG);
561 goto jleave;
562 }
563 }
564 }
565 #endif
566
567 /* We are prepared to ignore the namehint .. unless we may not */
568 if((oflags & mx_FS_O_SUFFIX) && *namehint != '\0'){
569 if((hintlen = su_cs_len(namehint)) >= maxname - a_RANDCHARS){
570 su_err_set_no(su_ERR_NAMETOOLONG);
571 goto jleave;
572 }
573 }else
574 hintlen = 0;
575
576 /* Prepare the template string once, then iterate over the random range.
577 * But first ensure we can report the name in !O_REGISTER cases ("hack") */
578 i = su_cs_len(tmpdir) + 1 + maxname +1;
579
580 if(!(oflags & mx_FS_O_REGISTER) && fstcp_or_nil != NIL){
581 union {struct a_fs_ent *fse; void *v; struct mx_fs_tmp_ctx *fstc;} p;
582
583 /* Store character data right after the struct */
584 p.v = n_autorec_alloc(sizeof(*p.fse) + i);
585 su_mem_set(p.fse, 0, sizeof(*p.fse));
586 p.fse->fse_realfile = R(char*,&p.fse[1]);
587 if(oflags & mx_FS_O_HOLDSIGS) /* Enable tmp_release() nonetheless? */
588 p.fse->fse_flags = a_FS_EF_HOLDSIGS;
589 *fstcp_or_nil = p.fstc;
590 }
591
592 cp_base = cp = n_lofi_alloc(i);
593 cp = su_cs_pcopy(cp, tmpdir);
594 *cp++ = '/';
595 /* C99 */{
596 uz j;
597 char *xp;
598
599 /* xxx We silently assume VAL_UAGENT easily fits in NAME_MAX-1 */
600 xp = su_cs_pcopy(cp, VAL_UAGENT);
601 *xp++ = '-';
602 if(!(oflags & mx_FS_O_SUFFIX))
603 xp = su_cs_pcopy(xp, namehint);
604
605 /* Just cut off as many of VAL_UAGENT as necessary */
606 if((i = P2UZ(xp - cp)) > (j = maxname - hintlen - a_RANDCHARS))
607 xp -= i - j;
608
609 if((oflags & mx_FS_O_SUFFIX) && hintlen > 0)
610 su_mem_copy(&xp[a_RANDCHARS], namehint, hintlen);
611
612 xp[hintlen + a_RANDCHARS] = '\0';
613 cp = xp;
614 }
615
616 osoflags = O_CREAT | O_EXCL | a_FS__O_CLOEXEC;
617 osoflags |= (oflags & mx_FS_O_WRONLY) ? O_WRONLY : O_RDWR;
618 if(oflags & mx_FS_O_APPEND)
619 osoflags |= O_APPEND;
620
621 for(relesigs = TRU1, i = 0;; ++i){
622 su_mem_copy(cp, mx_random_create_cp(a_RANDCHARS, NIL), a_RANDCHARS);
623
624 mx_sigs_all_holdx();
625
626 if((fd = open(cp_base, osoflags, 0600)) != -1){
627 if(mx_FS_FD_CLOEXEC_SET(fd))
628 break;
629 close(fd);
630 }
631
632 if(i >= mx_FS_TMP_OPEN_TRIES){
633 e = su_err_no();
634 goto jfree;
635 }
636
637 mx_sigs_all_rele();
638 }
639
640 /* C99 */{
641 char const *osflags;
642
643 osflags = (oflags & mx_FS_O_RDWR ? "w+" : "w");
644
645 if((fp = fdopen(fd, osflags)) != NIL){
646 if(oflags & mx_FS_O_REGISTER){
647 struct a_fs_ent *fsep;
648 int osflagbits;
649
650 a_fs_scan_mode(&osflags, &osflagbits); /* TODO osoflags&xy ?!!? */
651 fsep = a_fs_register_file(fp, osflagbits | a_FS__O_CLOEXEC, 0,
652 (a_FS_EF_RAW |
653 (oflags & mx_FS_O_REGISTER_UNLINK ? a_FS_EF_UNLINK : 0) |
654 (oflags & mx_FS_O_HOLDSIGS ? a_FS_EF_HOLDSIGS : 0)),
655 cp_base, 0L, NIL);
656
657 /* User arg points to registered data in this case */
658 if(fstcp_or_nil != NIL){
659 union {void *v; struct mx_fs_tmp_ctx *fstc;} p;
660
661 p.v = fsep;
662 *fstcp_or_nil = p.fstc;
663 }
664 }else if(fstcp_or_nil != NIL){
665 /* Otherwise copy filename buffer into autorec storage */
666 cp = UNCONST(char*,(*fstcp_or_nil)->fstc_filename);
667 su_cs_pcopy(cp, cp_base);
668 }
669 }
670 }
671
672 if(fp == NIL || (oflags & mx_FS_O_UNLINK)){
673 e = su_err_no();
674 unlink(cp_base);
675 if(fp == NIL)
676 close(fd);
677 goto jfree;
678 }else if(fp != NIL){
679 /* We will succeed and keep the file around for further usage, likely
680 * another stream will be opened for pure reading purposes (this is true
681 * at the time of this writing. A restrictive umask(2) settings may have
682 * turned the path inaccessible, so ensure it may be read at least!
683 * TODO once ok_vlook() can return an integer, look up *umask* first! */
684 (void)fchmod(fd, S_IWUSR | S_IRUSR);
685 }
686
687 n_lofi_free(cp_base);
688
689 jleave:
690 if(relesigs && (fp == NIL || !(oflags & mx_FS_O_HOLDSIGS)))
691 mx_sigs_all_rele();
692
693 if(fp == NIL){
694 su_err_set_no(e);
695 if(fstcp_or_nil != NIL)
696 *fstcp_or_nil = NIL;
697 }
698
699 NYD_OU;
700 return fp;
701
702 jfree:
703 if((cp = cp_base) != NIL)
704 n_lofi_free(cp);
705 goto jleave;
706 }
707
708 void
mx_fs_tmp_release(struct mx_fs_tmp_ctx * fstcp)709 mx_fs_tmp_release(struct mx_fs_tmp_ctx *fstcp){
710 union {void *vp; struct a_fs_ent *fsep;} u;
711 NYD_IN;
712
713 ASSERT(fstcp != NIL);
714
715 u.vp = fstcp;
716 ASSERT(u.fsep->fse_flags & a_FS_EF_HOLDSIGS);
717 DBG( if(u.fsep->fse_flags & a_FS_EF_UNLINK)
718 n_alert("tmp_release(): REGISTER_UNLINK set!"); )
719
720 unlink(u.fsep->fse_realfile);
721
722 u.fsep->fse_flags &= ~(a_FS_EF_HOLDSIGS | a_FS_EF_UNLINK);
723
724 mx_sigs_all_rele();
725
726 NYD_OU;
727 }
728
729 FILE *
mx_fs_fd_open(sz fd,char const * oflags,boole nocloexec)730 mx_fs_fd_open(sz fd, char const *oflags, boole nocloexec){
731 FILE *fp;
732 int osflags;
733 NYD_IN;
734
735 a_fs_scan_mode(&oflags, &osflags);
736 if(!nocloexec)
737 osflags |= a_FS__O_CLOEXEC; /* Ensured by caller as documented! */
738
739 if((fp = fdopen(S(int,fd), oflags)) != NIL && oflags != NIL)
740 a_fs_register_file(fp, osflags, 0, a_FS_EF_RAW, NIL, 0L, NIL);
741
742 NYD_OU;
743 return fp;
744 }
745
746 boole
mx_fs_fd_cloexec_set(sz fd)747 mx_fs_fd_cloexec_set(sz fd){
748 s32 ifd;
749 boole rv;
750 NYD2_IN;
751
752 ifd = S(s32,fd);
753 /*if(!(rv = ((a__fl = fcntl(ifd, F_GETFD)) != -1 && !(ifd & FD_CLOEXEC))))*/
754 rv = (fcntl(ifd, F_SETFD, FD_CLOEXEC) != -1);
755
756 NYD2_OU;
757 return rv;
758 }
759
760 boole
mx_fs_close(FILE * fp)761 mx_fs_close(FILE *fp){
762 boole rv;
763 NYD_IN;
764
765 rv = (a_fs_unregister_file(fp) && fclose(fp) == 0);
766
767 NYD_OU;
768 return rv;
769 }
770
771 boole
mx_fs_pipe_cloexec(sz fd[2])772 mx_fs_pipe_cloexec(sz fd[2]){
773 int xfd[2];
774 boole rv;
775 NYD_IN;
776
777 rv = FAL0;
778
779 #ifdef mx_HAVE_PIPE2
780 if(pipe2(xfd, O_CLOEXEC) != -1){
781 fd[0] = xfd[0];
782 fd[1] = xfd[1];
783 rv = TRU1;
784 }
785 #else
786 if(pipe(xfd) != -1){
787 fd[0] = xfd[0];
788 fd[1] = xfd[1];
789 mx_fs_fd_cloexec_set(fd[0]);
790 mx_fs_fd_cloexec_set(fd[1]);
791 rv = TRU1;
792 }
793 #endif
794
795 NYD_OU;
796 return rv;
797 }
798
799 FILE *
mx_fs_pipe_open(char const * cmd,char const * mode,char const * sh,char const ** env_addon,int newfd1)800 mx_fs_pipe_open(char const *cmd, char const *mode, char const *sh,
801 char const **env_addon, int newfd1){
802 struct mx_child_ctx cc;
803 sz p[2], myside, hisside, fd0, fd1;
804 sigset_t nset;
805 char mod[2];
806 FILE *rv;
807 NYD_IN;
808
809 rv = NIL;
810 mod[0] = '0', mod[1] = '\0';
811
812 if(!mx_fs_pipe_cloexec(p))
813 goto jleave;
814
815 if(*mode == 'r'){
816 myside = p[a_FS_PIPE_READ];
817 fd0 = mx_CHILD_FD_PASS;
818 hisside = fd1 = p[a_FS_PIPE_WRITE];
819 mod[0] = *mode;
820 }else if(*mode == 'W'){
821 myside = p[a_FS_PIPE_WRITE];
822 hisside = fd0 = p[a_FS_PIPE_READ];
823 fd1 = newfd1;
824 mod[0] = 'w';
825 }else{
826 myside = p[a_FS_PIPE_WRITE];
827 hisside = fd0 = p[a_FS_PIPE_READ];
828 fd1 = mx_CHILD_FD_PASS;
829 mod[0] = 'w';
830 }
831
832 sigemptyset(&nset);
833 mx_child_ctx_setup(&cc);
834 /* Be simple and let's just synchronize completely on that */
835 cc.cc_flags = ((cc.cc_cmd = cmd) == R(char*,-1)) ? mx_CHILD_SPAWN_CONTROL
836 : mx_CHILD_SPAWN_CONTROL | mx_CHILD_SPAWN_CONTROL_LINGER;
837 cc.cc_mask = &nset;
838 cc.cc_fds[mx_CHILD_FD_IN] = fd0;
839 cc.cc_fds[mx_CHILD_FD_OUT] = fd1;
840 cc.cc_env_addon = env_addon;
841
842 /* Is this a special in-process fork setup? */
843 if(cmd == R(char*,-1)){
844 if(!mx_child_fork(&cc))
845 n_perr(_("fork"), 0);
846 else if(cc.cc_pid == 0){
847 union {char const *ccp; int (*ptf)(void); int es;} u;
848
849 mx_child_in_child_setup(&cc);
850 close(S(int,p[a_FS_PIPE_READ]));
851 close(S(int,p[a_FS_PIPE_WRITE]));
852 /* TODO close all other open FDs except stds and reset memory */
853 /* Standard I/O drives me insane! All we need is a sync operation
854 * that causes n_stdin to forget about any read buffer it may have.
855 * We cannot use fflush(3), this works with Musl and Solaris, but not
856 * with GlibC. (For at least pipes.) We cannot use fdreopen(),
857 * because this function does not exist! Luckily (!!!) we only use
858 * n_stdin not stdin in our child, otherwise all bets were off!
859 * TODO (Unless we would fiddle around with FILE* directly:
860 * TODO #ifdef __GLIBC__
861 * TODO n_stdin->_IO_read_ptr = n_stdin->_IO_read_end;
862 * TODO #elif *BSD*
863 * TODO n_stdin->_r = 0;
864 * TODO #elif su_OS_SOLARIS || su_OS_SUNOS
865 * TODO n_stdin->_cnt = 0;
866 * TODO #endif
867 * TODO ) which should have additional config test for sure! */
868 n_stdin = fdopen(STDIN_FILENO, "r");
869 /*n_stdout = fdopen(STDOUT_FILENO, "w");*/
870 /*n_stderr = fdopen(STDERR_FILENO, "w");*/
871 u.ccp = sh;
872 u.es = (*u.ptf)();
873 /*fflush(NULL);*/
874 for(;;)
875 _exit(u.es);
876 }
877 }else{
878 if(sh != NIL){
879 cc.cc_cmd = sh;
880 cc.cc_args[0] = "-c";
881 cc.cc_args[1] = cmd;
882 }
883
884 mx_child_run(&cc);
885 }
886
887 if(cc.cc_pid < 0){
888 close(S(int,p[a_FS_PIPE_READ]));
889 close(S(int,p[a_FS_PIPE_WRITE]));
890 goto jleave;
891 }
892
893 close(S(int,hisside));
894 if((rv = fdopen(myside, mod)) != NIL)
895 a_fs_register_file(rv, 0, &cc,
896 ((fd0 != mx_CHILD_FD_PASS && fd1 != mx_CHILD_FD_PASS)
897 ? a_FS_EF_PIPE : a_FS_EF_PIPE | a_FS_EF_FD_PASS_NEEDS_WAIT),
898 NIL, 0L, NIL);
899 else
900 close(myside);
901
902 jleave:
903 NYD_OU;
904 return rv;
905 }
906
907 s32
mx_fs_pipe_signal(FILE * fp,s32 sig)908 mx_fs_pipe_signal(FILE *fp, s32 sig){
909 s32 rv;
910 struct a_fs_ent *fsep;
911 NYD_IN;
912
913 for(fsep = a_fs_fp_head;; fsep = fsep->fse_link)
914 if(fsep == NIL){
915 rv = -1;
916 break;
917 }else if(fsep->fse_fp == fp){
918 rv = mx_child_signal(&fsep->fse_cc, sig);
919 break;
920 }
921
922 NYD_OU;
923 return rv;
924 }
925
926 boole
mx_fs_pipe_close(FILE * ptr,boole dowait)927 mx_fs_pipe_close(FILE *ptr, boole dowait){
928 n_sighdl_t opipe;
929 struct mx_child_ctx cc;
930 boole rv;
931 struct a_fs_ent *fsep;
932 NYD_IN;
933
934 for(fsep = a_fs_fp_head;; fsep = fsep->fse_link)
935 if(UNLIKELY(fsep == NIL)){
936 DBG( n_alert(_("pipe_close: invalid file pointer")); )
937 rv = FAL0;
938 goto jleave;
939 }else if(fsep->fse_fp == ptr)
940 break;
941 cc = fsep->fse_cc;
942
943 ASSERT(!(fsep->fse_flags & a_FS_EF_FD_PASS_NEEDS_WAIT) || dowait);
944
945 a_fs_unregister_file(ptr);
946
947 opipe = safe_signal(SIGPIPE, SIG_IGN); /* TODO only because we jump stuff */
948 fclose(ptr);
949 safe_signal(SIGPIPE, opipe);
950
951 if(dowait)
952 rv = (mx_child_wait(&cc) && cc.cc_exit_status == 0);
953 else{
954 mx_child_forget(&cc);
955 rv = TRU1;
956 }
957
958 jleave:
959 NYD_OU;
960 return rv;
961 }
962
963 boole
mx_fs_flush(FILE * fp)964 mx_fs_flush(FILE *fp){
965 boole rv;
966 NYD_IN;
967
968 rv = (fflush(fp) != EOF);
969
970 NYD_OU;
971 return rv;
972 }
973
974 void
mx_fs_close_all(void)975 mx_fs_close_all(void){
976 NYD_IN;
977
978 while(a_fs_fp_head != NIL)
979 if((a_fs_fp_head->fse_flags & a_FS_EF_MASK) == a_FS_EF_PIPE)
980 mx_fs_pipe_close(a_fs_fp_head->fse_fp, TRU1);
981 else
982 mx_fs_close(a_fs_fp_head->fse_fp);
983
984 NYD_OU;
985 }
986
987 void
mx_fs_linepool_aquire(char ** dpp,uz * dsp)988 mx_fs_linepool_aquire(char **dpp, uz *dsp){
989 struct a_fs_lpool_ent *lpep;
990 NYD2_IN;
991
992 if((lpep = a_fs_lpool_free) != NIL)
993 a_fs_lpool_free = lpep->fsle_last;
994 else
995 lpep = su_TCALLOC(struct a_fs_lpool_ent, 1);
996
997 lpep->fsle_last = a_fs_lpool_used;
998 a_fs_lpool_used = lpep;
999 *dpp = lpep->fsle_dat;
1000 lpep->fsle_dat = NIL;
1001 *dsp = lpep->fsle_size;
1002 lpep->fsle_size = 0;
1003
1004 NYD2_OU;
1005 }
1006
1007 void
mx_fs_linepool_release(char * dp,uz ds)1008 mx_fs_linepool_release(char *dp, uz ds){
1009 struct a_fs_lpool_ent *lpep;
1010 NYD2_IN;
1011
1012 ASSERT(a_fs_lpool_used != NIL);
1013 lpep = a_fs_lpool_used;
1014 a_fs_lpool_used = lpep->fsle_last;
1015
1016 lpep->fsle_last = a_fs_lpool_free;
1017 a_fs_lpool_free = lpep;
1018 lpep->fsle_dat = dp;
1019 lpep->fsle_size = ds;
1020
1021 NYD2_OU;
1022 }
1023
boole(mx_fs_linepool_book)1024 boole
1025 (mx_fs_linepool_book)(char **dp, uz *dsp, uz len, uz toadd
1026 su_DBG_LOC_ARGS_DECL){
1027 boole rv;
1028 NYD2_IN;
1029
1030 rv = FAL0;
1031
1032 if(UZ_MAX - 192 <= toadd)
1033 goto jeoverflow;
1034 toadd = (toadd + 192) & ~127;
1035
1036 if(!su_mem_get_can_book(1, len, toadd))
1037 goto jeoverflow;
1038 len += toadd;
1039
1040 if(*dsp < len){
1041 char *ndp;
1042
1043 if((ndp = su_MEM_REALLOC_LOCOR(*dp, len, su_DBG_LOC_ARGS_ORUSE)) == NIL)
1044 goto jleave;
1045 *dp = ndp;
1046 *dsp = len;
1047 }
1048
1049 rv = TRU1;
1050 jleave:
1051 NYD2_OU;
1052 return rv;
1053
1054 jeoverflow:
1055 su_state_err(su_STATE_ERR_OVERFLOW, (su_STATE_ERR_PASS |
1056 su_STATE_ERR_NOERRNO), _("fs_linepool_book(): buffer size overflow"));
1057 goto jleave;
1058 }
1059
1060 void
mx_fs_linepool_cleanup(boole completely)1061 mx_fs_linepool_cleanup(boole completely){
1062 struct a_fs_lpool_ent *lpep, *tmp, *keep;
1063 NYD2_IN;
1064
1065 if(!completely)
1066 completely = 3; /* XXX magic */
1067 else
1068 completely = FAL0;
1069
1070 lpep = a_fs_lpool_free;
1071 a_fs_lpool_free = keep = NIL;
1072 jredo:
1073 while((tmp = lpep) != NIL){
1074 void *vp;
1075
1076 lpep = lpep->fsle_last;
1077
1078 if((vp = tmp->fsle_dat) != NIL && completely &&
1079 tmp->fsle_size <= LINESIZE * 2){
1080 --completely;
1081 tmp->fsle_last = a_fs_lpool_free;
1082 a_fs_lpool_free = tmp;
1083 }else{
1084 if(vp != NIL)
1085 su_FREE(vp);
1086 su_FREE(tmp);
1087 }
1088 }
1089
1090 /* Only called from go_main_loop(), save to throw the hulls away */
1091 if((lpep = a_fs_lpool_used) != NIL){
1092 a_fs_lpool_used = NIL;
1093 goto jredo;
1094 }
1095
1096 NYD2_OU;
1097 }
1098
1099 /* TODO The rest below is old-style v15 */
1100
1101 /* line is a buffer with the result of fgets(). Returns the first newline or
1102 * the last character read */
1103 static uz _length_of_line(char const *line, uz linesize);
1104
1105 /* Read a line, one character at a time */
1106 static char *a_fs_fgetline_byone(char **line, uz *linesize, uz *llen_or_nil,
1107 FILE *fp, int appendnl, uz n su_DBG_LOC_ARGS_DECL);
1108
1109 static uz
_length_of_line(char const * line,uz linesize)1110 _length_of_line(char const *line, uz linesize)
1111 {
1112 uz i;
1113 NYD2_IN;
1114
1115 /* Last character is always '\0' and was added by fgets() */
1116 for (--linesize, i = 0; i < linesize; i++)
1117 if (line[i] == '\n')
1118 break;
1119 i = (i < linesize) ? i + 1 : linesize;
1120
1121 NYD2_OU;
1122 return i;
1123 }
1124
1125 static char *
a_fs_fgetline_byone(char ** line,uz * linesize,uz * llen_or_nil,FILE * fp,int appendnl,uz n su_DBG_LOC_ARGS_DECL)1126 a_fs_fgetline_byone(char **line, uz *linesize, uz *llen_or_nil, FILE *fp,
1127 int appendnl, uz n su_DBG_LOC_ARGS_DECL)
1128 {
1129 char *rv;
1130 int c;
1131 NYD2_IN;
1132
1133 ASSERT(*linesize == 0 || *line != NULL);
1134
1135 /* Always leave room for NETNL, not only \n */
1136 for (rv = *line;;) {
1137 if (*linesize <= LINESIZE || n >= *linesize - 128) {
1138 *linesize += ((rv == NULL) ? LINESIZE + n + 2 : 256);
1139 *line = rv = su_MEM_REALLOC_LOCOR(rv, *linesize,
1140 su_DBG_LOC_ARGS_ORUSE);
1141 }
1142 c = getc(fp);
1143 if (c != EOF) {
1144 rv[n++] = c;
1145 rv[n] = '\0';
1146 if (c == '\n') {
1147 n_pstate |= n_PS_READLINE_NL;
1148 break;
1149 }
1150 } else {
1151 if (n > 0 && feof(fp)) {
1152 if (appendnl) {
1153 rv[n++] = '\n';
1154 rv[n] = '\0';
1155 }
1156 break;
1157 } else {
1158 rv = NULL;
1159 goto jleave;
1160 }
1161 }
1162 }
1163
1164 if(llen_or_nil != NIL)
1165 *llen_or_nil = n;
1166
1167 jleave:
1168 NYD2_OU;
1169 return rv;
1170 }
1171
1172 char *
1173 (fgetline)(char **line, uz *linesize, uz *cnt, uz *llen_or_nil, FILE *fp,
1174 int appendnl su_DBG_LOC_ARGS_DECL)
1175 {
1176 uz i_llen, size;
1177 char *rv;
1178 NYD2_IN;
1179
1180 n_pstate &= ~n_PS_READLINE_NL;
1181
1182 if(llen_or_nil != NIL)
1183 *llen_or_nil = 0;
1184
1185 if(cnt == NIL){
1186 /* Without we cannot determine where the chars returned by fgets()
1187 * end if there's no newline. We have to read one character by one */
1188 rv = a_fs_fgetline_byone(line, linesize, llen_or_nil, fp, appendnl, 0
1189 su_DBG_LOC_ARGS_USE);
1190 goto jleave;
1191 }
1192
1193 if ((rv = *line) == NULL || *linesize < LINESIZE)
1194 *line = rv = su_MEM_REALLOC_LOCOR(rv, *linesize = LINESIZE,
1195 su_DBG_LOC_ARGS_ORUSE);
1196 size = (*linesize <= *cnt) ? *linesize : *cnt + 1;
1197 if (size <= 1 || fgets(rv, size, fp) == NULL) {
1198 /* Leave llen untouched; it is used to determine whether the last line
1199 * was \n-terminated in some callers */
1200 rv = NULL;
1201 goto jleave;
1202 }
1203
1204 i_llen = _length_of_line(rv, size);
1205 *cnt -= i_llen;
1206 while (rv[i_llen - 1] != '\n') {
1207 *line = rv = su_MEM_REALLOC_LOCOR(rv, *linesize += 256,
1208 su_DBG_LOC_ARGS_ORUSE);
1209 size = *linesize - i_llen;
1210 size = (size <= *cnt) ? size : *cnt + 1;
1211 if (size <= 1) {
1212 if (appendnl) {
1213 rv[i_llen++] = '\n';
1214 rv[i_llen] = '\0';
1215 }
1216 break;
1217 } else if (fgets(rv + i_llen, size, fp) == NULL) {
1218 if (!feof(fp)) {
1219 rv = NULL;
1220 goto jleave;
1221 }
1222 if (appendnl) {
1223 rv[i_llen++] = '\n';
1224 rv[i_llen] = '\0';
1225 }
1226 break;
1227 }
1228 size = _length_of_line(rv + i_llen, size);
1229 i_llen += size;
1230 *cnt -= size;
1231 }
1232
1233 /* Always leave room for NETNL, not only \n */
1234 if(appendnl && *linesize - i_llen < 3)
1235 *line = rv = su_MEM_REALLOC_LOCOR(rv, *linesize += 256,
1236 su_DBG_LOC_ARGS_ORUSE);
1237
1238 if(llen_or_nil != NIL)
1239 *llen_or_nil = i_llen;
1240 jleave:
1241 NYD2_OU;
1242 return rv;
1243 }
1244
1245 int
1246 (readline_restart)(FILE *ibuf, char **linebuf, uz *linesize, uz n
1247 su_DBG_LOC_ARGS_DECL)
1248 {
1249 /* TODO readline_restart(): always *appends* LF just to strip it again;
1250 * TODO should be configurable just as for fgetline(); ..or whatever..
1251 * TODO intwrap */
1252 int rv = -1;
1253 long size;
1254 NYD2_IN;
1255
1256 clearerr(ibuf);
1257
1258 /* Interrupts will cause trouble if we are inside a stdio call. As this is
1259 * only relevant if input is from tty, bypass it by read(), then */
1260 if ((n_psonce & n_PSO_TTYIN) && fileno(ibuf) == 0) {
1261 ASSERT(*linesize == 0 || *linebuf != NULL);
1262 n_pstate &= ~n_PS_READLINE_NL;
1263 for (;;) {
1264 if (*linesize <= LINESIZE || n >= *linesize - 128) {
1265 *linesize += ((*linebuf == NULL) ? LINESIZE + n + 1 : 256);
1266 *linebuf = su_MEM_REALLOC_LOCOR(*linebuf, *linesize,
1267 su_DBG_LOC_ARGS_ORUSE);
1268 }
1269 jagain:
1270 size = read(0, *linebuf + n, *linesize - n - 1);
1271 if (size > 0) {
1272 n += size;
1273 (*linebuf)[n] = '\0';
1274 if ((*linebuf)[n - 1] == '\n') {
1275 n_pstate |= n_PS_READLINE_NL;
1276 break;
1277 }
1278 } else {
1279 if (size == -1 && su_err_no() == su_ERR_INTR)
1280 goto jagain;
1281 /* TODO eh. what is this? that now supposed to be a line?!? */
1282 if (n > 0) {
1283 if ((*linebuf)[n - 1] != '\n') {
1284 (*linebuf)[n++] = '\n';
1285 (*linebuf)[n] = '\0';
1286 } else
1287 n_pstate |= n_PS_READLINE_NL;
1288 break;
1289 } else
1290 goto jleave;
1291 }
1292 }
1293 } else {
1294 /* Not reading from standard input or standard input not a terminal. We
1295 * read one char at a time as it is the only way to get lines with
1296 * embedded NUL characters in standard stdio */
1297 if(a_fs_fgetline_byone(linebuf, linesize, &n, ibuf, 1,
1298 n su_DBG_LOC_ARGS_USE) == NIL)
1299 goto jleave;
1300 }
1301 if (n > 0 && (*linebuf)[n - 1] == '\n')
1302 (*linebuf)[--n] = '\0';
1303
1304 rv = (int)n;
1305 jleave:
1306 NYD2_OU;
1307 return rv;
1308 }
1309
1310 off_t
fsize(FILE * iob)1311 fsize(FILE *iob)
1312 {
1313 struct stat sbuf;
1314 off_t rv;
1315 NYD_IN;
1316
1317 rv = (fstat(fileno(iob), &sbuf) == -1) ? 0 : sbuf.st_size;
1318
1319 NYD_OU;
1320 return rv;
1321 }
1322
1323 #include "su/code-ou.h"
1324 /* s-it-mode */
1325