1 /*        $NetBSD: shf.c,v 1.16 2024/02/05 21:20:09 andvar Exp $      */
2 
3 /*
4  *  Shell file I/O routines
5  */
6 #include <sys/cdefs.h>
7 
8 #ifndef lint
9 __RCSID("$NetBSD: shf.c,v 1.16 2024/02/05 21:20:09 andvar Exp $");
10 #endif
11 
12 #include <sys/stat.h>
13 
14 #include "sh.h"
15 #include "ksh_limval.h"
16 
17 
18 /* flags to shf_emptybuf() */
19 #define EB_READSW   0x01      /* about to switch to reading */
20 #define EB_GROW               0x02      /* grow buffer if necessary (STRING+DYNAMIC) */
21 
22 /*
23  * Replacement stdio routines.  Stdio is too flakey on too many machines
24  * to be useful when you have multiple processes using the same underlying
25  * file descriptors.
26  */
27 
28 static int          shf_fillbuf         ARGS((struct shf *shf));
29 static int          shf_emptybuf        ARGS((struct shf *shf, int flags));
30 
31 /* Open a file.  First three args are for open(), last arg is flags for
32  * this package.  Returns NULL if file could not be opened, or if a dup
33  * fails.
34  */
35 struct shf *
shf_open(name,oflags,mode,sflags)36 shf_open(name, oflags, mode, sflags)
37           const char *name;
38           int oflags;
39           int mode;
40           int sflags;
41 {
42           struct shf *shf;
43           int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
44           int fd;
45 
46           /* Done before open so if alloca fails, fd won't be lost. */
47           shf = (struct shf *) alloc(sizeof(struct shf) + bsize, ATEMP);
48           shf->areap = ATEMP;
49           shf->buf = (unsigned char *) &shf[1];
50           shf->bsize = bsize;
51           shf->flags = SHF_ALLOCS;
52           /* Rest filled in by reopen. */
53 
54           fd = open(name, oflags, mode);
55           if (fd < 0) {
56                     afree(shf, shf->areap);
57                     return NULL;
58           }
59           if ((sflags & SHF_MAPHI) && fd < FDBASE) {
60                     int nfd;
61 
62                     nfd = ksh_dupbase(fd, FDBASE);
63                     close(fd);
64                     if (nfd < 0) {
65                               afree(shf, shf->areap);
66                               return NULL;
67                     }
68                     fd = nfd;
69           }
70           sflags &= ~SHF_ACCMODE;
71           sflags |= (oflags & O_ACCMODE) == O_RDONLY ? SHF_RD
72                       : ((oflags & O_ACCMODE) == O_WRONLY ? SHF_WR
73                          : SHF_RDWR);
74 
75           return shf_reopen(fd, sflags, shf);
76 }
77 
78 /* Set up the shf structure for a file descriptor.  Doesn't fail. */
79 struct shf *
shf_fdopen(fd,sflags,shf)80 shf_fdopen(fd, sflags, shf)
81           int fd;
82           int sflags;
83           struct shf *shf;
84 {
85           int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
86 
87           /* use fcntl() to figure out correct read/write flags */
88           if (sflags & SHF_GETFL) {
89                     int flags = fcntl(fd, F_GETFL, 0);
90 
91                     if (flags < 0)
92                               /* will get an error on first read/write */
93                               sflags |= SHF_RDWR;
94                     else
95                               switch (flags & O_ACCMODE) {
96                               case O_RDONLY: sflags |= SHF_RD; break;
97                               case O_WRONLY: sflags |= SHF_WR; break;
98                               case O_RDWR: sflags |= SHF_RDWR; break;
99                               }
100           }
101 
102           if (!(sflags & (SHF_RD | SHF_WR)))
103                     internal_errorf(1, "shf_fdopen: missing read/write");
104 
105           if (shf) {
106                     if (bsize) {
107                               shf->buf = (unsigned char *) alloc(bsize, ATEMP);
108                               sflags |= SHF_ALLOCB;
109                     } else
110                               shf->buf = (unsigned char *) 0;
111           } else {
112                     shf = (struct shf *) alloc(sizeof(struct shf) + bsize, ATEMP);
113                     shf->buf = (unsigned char *) &shf[1];
114                     sflags |= SHF_ALLOCS;
115           }
116           shf->areap = ATEMP;
117           shf->fd = fd;
118           shf->rp = shf->wp = shf->buf;
119           shf->rnleft = 0;
120           shf->rbsize = bsize;
121           shf->wnleft = 0; /* force call to shf_emptybuf() */
122           shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;
123           shf->flags = sflags;
124           shf->errno_ = 0;
125           shf->bsize = bsize;
126           if (sflags & SHF_CLEXEC)
127                     fd_clexec(fd);
128           return shf;
129 }
130 
131 /* Set up an existing shf (and buffer) to use the given fd */
132 struct shf *
shf_reopen(fd,sflags,shf)133 shf_reopen(fd, sflags, shf)
134           int fd;
135           int sflags;
136           struct shf *shf;
137 {
138           int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
139 
140           /* use fcntl() to figure out correct read/write flags */
141           if (sflags & SHF_GETFL) {
142                     int flags = fcntl(fd, F_GETFL, 0);
143 
144                     if (flags < 0)
145                               /* will get an error on first read/write */
146                               sflags |= SHF_RDWR;
147                     else
148                               switch (flags & O_ACCMODE) {
149                               case O_RDONLY: sflags |= SHF_RD; break;
150                               case O_WRONLY: sflags |= SHF_WR; break;
151                               case O_RDWR: sflags |= SHF_RDWR; break;
152                               }
153           }
154 
155           if (!(sflags & (SHF_RD | SHF_WR)))
156                     internal_errorf(1, "shf_reopen: missing read/write");
157           if (!shf || !shf->buf || shf->bsize < bsize)
158                     internal_errorf(1, "shf_reopen: bad shf/buf/bsize");
159 
160           /* assumes shf->buf and shf->bsize already set up */
161           shf->fd = fd;
162           shf->rp = shf->wp = shf->buf;
163           shf->rnleft = 0;
164           shf->rbsize = bsize;
165           shf->wnleft = 0; /* force call to shf_emptybuf() */
166           shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;
167           shf->flags = (shf->flags & (SHF_ALLOCS | SHF_ALLOCB)) | sflags;
168           shf->errno_ = 0;
169           if (sflags & SHF_CLEXEC)
170                     fd_clexec(fd);
171           return shf;
172 }
173 
174 /* Open a string for reading or writing.  If reading, bsize is the number
175  * of bytes that can be read.  If writing, bsize is the maximum number of
176  * bytes that can be written.  If shf is not null, it is filled in and
177  * returned, if it is null, shf is allocated.  If writing and buf is null
178  * and SHF_DYNAMIC is set, the buffer is allocated (if bsize > 0, it is
179  * used for the initial size).  Doesn't fail.
180  * When writing, a byte is reserved for a trailing null - see shf_sclose().
181  */
182 struct shf *
shf_sopen(buf,bsize,sflags,shf)183 shf_sopen(buf, bsize, sflags, shf)
184           char *buf;
185           int bsize;
186           int sflags;
187           struct shf *shf;
188 {
189           /* can't have a read+write string */
190           if (!(sflags & (SHF_RD | SHF_WR))
191               || (sflags & (SHF_RD | SHF_WR)) == (SHF_RD | SHF_WR))
192                     internal_errorf(1, "shf_sopen: flags 0x%x", sflags);
193 
194           if (!shf) {
195                     shf = (struct shf *) alloc(sizeof(struct shf), ATEMP);
196                     sflags |= SHF_ALLOCS;
197           }
198           shf->areap = ATEMP;
199           if (!buf && (sflags & SHF_WR) && (sflags & SHF_DYNAMIC)) {
200                     if (bsize <= 0)
201                               bsize = 64;
202                     sflags |= SHF_ALLOCB;
203                     buf = alloc(bsize, shf->areap);
204           }
205           shf->fd = -1;
206           shf->buf = shf->rp = shf->wp = (unsigned char *) buf;
207           shf->rnleft = bsize;
208           shf->rbsize = bsize;
209           shf->wnleft = bsize - 1;      /* space for a '\0' */
210           shf->wbsize = bsize;
211           shf->flags = sflags | SHF_STRING;
212           shf->errno_ = 0;
213           shf->bsize = bsize;
214 
215           return shf;
216 }
217 
218 /* Flush and close file descriptor, free the shf structure */
219 int
shf_close(shf)220 shf_close(shf)
221           struct shf *shf;
222 {
223           int ret = 0;
224 
225           if (shf->fd >= 0) {
226                     ret = shf_flush(shf);
227                     if (close(shf->fd) < 0)
228                               ret = EOF;
229           }
230           if (shf->flags & SHF_ALLOCS)
231                     afree(shf, shf->areap);
232           else if (shf->flags & SHF_ALLOCB)
233                     afree(shf->buf, shf->areap);
234 
235           return ret;
236 }
237 
238 /* Flush and close file descriptor, don't free file structure */
239 int
shf_fdclose(shf)240 shf_fdclose(shf)
241           struct shf *shf;
242 {
243           int ret = 0;
244 
245           if (shf->fd >= 0) {
246                     ret = shf_flush(shf);
247                     if (close(shf->fd) < 0)
248                               ret = EOF;
249                     shf->rnleft = 0;
250                     shf->rp = shf->buf;
251                     shf->wnleft = 0;
252                     shf->fd = -1;
253           }
254 
255           return ret;
256 }
257 
258 /* Close a string - if it was opened for writing, it is null terminated;
259  * returns a pointer to the string and frees shf if it was allocated
260  * (does not free string if it was allocated).
261  */
262 char *
shf_sclose(shf)263 shf_sclose(shf)
264           struct shf *shf;
265 {
266           unsigned char *s = shf->buf;
267 
268           /* null terminate */
269           if (shf->flags & SHF_WR) {
270                     shf->wnleft++;
271                     shf_putc('\0', shf);
272           }
273           if (shf->flags & SHF_ALLOCS)
274                     afree(shf, shf->areap);
275           return (char *) s;
276 }
277 
278 /* Flush and free file structure, don't close file descriptor */
279 int
shf_finish(shf)280 shf_finish(shf)
281           struct shf *shf;
282 {
283           int ret = 0;
284 
285           if (shf->fd >= 0)
286                     ret = shf_flush(shf);
287           if (shf->flags & SHF_ALLOCS)
288                     afree(shf, shf->areap);
289           else if (shf->flags & SHF_ALLOCB)
290                     afree(shf->buf, shf->areap);
291 
292           return ret;
293 }
294 
295 /* Un-read what has been read but not examined, or write what has been
296  * buffered.  Returns 0 for success, EOF for (write) error.
297  */
298 int
shf_flush(shf)299 shf_flush(shf)
300           struct shf *shf;
301 {
302           if (shf->flags & SHF_STRING)
303                     return (shf->flags & SHF_WR) ? EOF : 0;
304 
305           if (shf->fd < 0)
306                     internal_errorf(1, "shf_flush: no fd");
307 
308           if (shf->flags & SHF_ERROR) {
309                     errno = shf->errno_;
310                     return EOF;
311           }
312 
313           if (shf->flags & SHF_READING) {
314                     shf->flags &= ~(SHF_EOF | SHF_READING);
315                     if (shf->rnleft > 0) {
316                               lseek(shf->fd, (off_t) -shf->rnleft, 1);
317                               shf->rnleft = 0;
318                               shf->rp = shf->buf;
319                     }
320                     return 0;
321           } else if (shf->flags & SHF_WRITING)
322                     return shf_emptybuf(shf, 0);
323 
324           return 0;
325 }
326 
327 /* Write out any buffered data.  If currently reading, flushes the read
328  * buffer.  Returns 0 for success, EOF for (write) error.
329  */
330 static int
shf_emptybuf(shf,flags)331 shf_emptybuf(shf, flags)
332           struct shf *shf;
333           int flags;
334 {
335           int ret = 0;
336 
337           if (!(shf->flags & SHF_STRING) && shf->fd < 0)
338                     internal_errorf(1, "shf_emptybuf: no fd");
339 
340           if (shf->flags & SHF_ERROR) {
341                     errno = shf->errno_;
342                     return EOF;
343           }
344 
345           if (shf->flags & SHF_READING) {
346                     if (flags & EB_READSW) /* doesn't happen */
347                               return 0;
348                     ret = shf_flush(shf);
349                     shf->flags &= ~SHF_READING;
350           }
351           if (shf->flags & SHF_STRING) {
352                     unsigned char       *nbuf;
353 
354                     /* Note that we assume SHF_ALLOCS is not set if SHF_ALLOCB
355                      * is set... (changing the shf pointer could cause problems)
356                      */
357                     if (!(flags & EB_GROW) || !(shf->flags & SHF_DYNAMIC)
358                         || !(shf->flags & SHF_ALLOCB))
359                               return EOF;
360                     /* allocate more space for buffer */
361                     nbuf = (unsigned char *) aresize(shf->buf, shf->wbsize * 2,
362                                                             shf->areap);
363                     shf->rp = nbuf + (shf->rp - shf->buf);
364                     shf->wp = nbuf + (shf->wp - shf->buf);
365                     shf->rbsize += shf->wbsize;
366                     shf->wnleft += shf->wbsize;
367                     shf->wbsize *= 2;
368                     shf->buf = nbuf;
369           } else {
370                     if (shf->flags & SHF_WRITING) {
371                               int ntowrite = shf->wp - shf->buf;
372                               unsigned char *buf = shf->buf;
373                               int n;
374 
375                               while (ntowrite > 0) {
376                                         n = write(shf->fd, buf, ntowrite);
377                                         if (n < 0) {
378                                                   if (errno == EINTR
379                                                       && !(shf->flags & SHF_INTERRUPT))
380                                                             continue;
381                                                   shf->flags |= SHF_ERROR;
382                                                   shf->errno_ = errno;
383                                                   shf->wnleft = 0;
384                                                   if (buf != shf->buf) {
385                                                             /* allow a second flush
386                                                              * to work */
387                                                             memmove(shf->buf, buf,
388                                                                       ntowrite);
389                                                             shf->wp = shf->buf + ntowrite;
390                                                   }
391                                                   return EOF;
392                                         }
393                                         buf += n;
394                                         ntowrite -= n;
395                               }
396                               if (flags & EB_READSW) {
397                                         shf->wp = shf->buf;
398                                         shf->wnleft = 0;
399                                         shf->flags &= ~SHF_WRITING;
400                                         return 0;
401                               }
402                     }
403                     shf->wp = shf->buf;
404                     shf->wnleft = shf->wbsize;
405           }
406           shf->flags |= SHF_WRITING;
407 
408           return ret;
409 }
410 
411 /* Fill up a read buffer.  Returns EOF for a read error, 0 otherwise. */
412 static int
shf_fillbuf(shf)413 shf_fillbuf(shf)
414           struct shf *shf;
415 {
416           if (shf->flags & SHF_STRING)
417                     return 0;
418 
419           if (shf->fd < 0)
420                     internal_errorf(1, "shf_fillbuf: no fd");
421 
422           if (shf->flags & (SHF_EOF | SHF_ERROR)) {
423                     if (shf->flags & SHF_ERROR)
424                               errno = shf->errno_;
425                     return EOF;
426           }
427 
428           if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF)
429                     return EOF;
430 
431           shf->flags |= SHF_READING;
432 
433           shf->rp = shf->buf;
434           while (1) {
435                     shf->rnleft = blocking_read(shf->fd, (char *) shf->buf,
436                                                       shf->rbsize);
437                     if (shf->rnleft < 0 && errno == EINTR
438                         && !(shf->flags & SHF_INTERRUPT))
439                               continue;
440                     break;
441           }
442           if (shf->rnleft <= 0) {
443                     if (shf->rnleft < 0) {
444                               shf->flags |= SHF_ERROR;
445                               shf->errno_ = errno;
446                               shf->rnleft = 0;
447                               shf->rp = shf->buf;
448                               return EOF;
449                     }
450                     shf->flags |= SHF_EOF;
451           }
452           return 0;
453 }
454 
455 /* Seek to a new position in the file.  If writing, flushes the buffer
456  * first.  If reading, optimizes small relative seeks that stay inside the
457  * buffer.  Returns 0 for success, EOF otherwise.
458  */
459 int
shf_seek(shf,where,from)460 shf_seek(shf, where, from)
461           struct shf *shf;
462           off_t where;
463           int from;
464 {
465           if (shf->fd < 0) {
466                     errno = EINVAL;
467                     return EOF;
468           }
469 
470           if (shf->flags & SHF_ERROR) {
471                     errno = shf->errno_;
472                     return EOF;
473           }
474 
475           if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF)
476                     return EOF;
477 
478           if (shf->flags & SHF_READING) {
479                     if (from == SEEK_CUR &&
480                                         (where < 0 ?
481                                                   -where >= shf->rbsize - shf->rnleft :
482                                                   where < shf->rnleft)) {
483                               shf->rnleft -= where;
484                               shf->rp += where;
485                               return 0;
486                     }
487                     shf->rnleft = 0;
488                     shf->rp = shf->buf;
489           }
490 
491           shf->flags &= ~(SHF_EOF | SHF_READING | SHF_WRITING);
492 
493           if (lseek(shf->fd, where, from) < 0) {
494                     shf->errno_ = errno;
495                     shf->flags |= SHF_ERROR;
496                     return EOF;
497           }
498 
499           return 0;
500 }
501 
502 
503 /* Read a buffer from shf.  Returns the number of bytes read into buf,
504  * if no bytes were read, returns 0 if end of file was seen, EOF if
505  * a read error occurred.
506  */
507 int
shf_read(buf,bsize,shf)508 shf_read(buf, bsize, shf)
509           char *buf;
510           int bsize;
511           struct shf *shf;
512 {
513           int orig_bsize = bsize;
514           int ncopy;
515 
516           if (!(shf->flags & SHF_RD))
517                     internal_errorf(1, "shf_read: flags %x", shf->flags);
518 
519           if (bsize <= 0)
520                     internal_errorf(1, "shf_read: bsize %d", bsize);
521 
522           while (bsize > 0) {
523                     if (shf->rnleft == 0
524                         && (shf_fillbuf(shf) == EOF || shf->rnleft == 0))
525                               break;
526                     ncopy = shf->rnleft;
527                     if (ncopy > bsize)
528                               ncopy = bsize;
529                     memcpy(buf, shf->rp, ncopy);
530                     buf += ncopy;
531                     bsize -= ncopy;
532                     shf->rp += ncopy;
533                     shf->rnleft -= ncopy;
534           }
535           /* Note: fread(3S) returns 0 for errors - this doesn't */
536           return orig_bsize == bsize ? (shf_error(shf) ? EOF : 0)
537                                            : orig_bsize - bsize;
538 }
539 
540 /* Read up to a newline or EOF.  The newline is put in buf; buf is always
541  * null terminated.  Returns NULL on read error or if nothing was read before
542  * end of file, returns a pointer to the null byte in buf otherwise.
543  */
544 char *
shf_getse(buf,bsize,shf)545 shf_getse(buf, bsize, shf)
546           char *buf;
547           int bsize;
548           struct shf *shf;
549 {
550           unsigned char *end;
551           int ncopy;
552           char *orig_buf = buf;
553 
554           if (!(shf->flags & SHF_RD))
555                     internal_errorf(1, "shf_getse: flags %x", shf->flags);
556 
557           if (bsize <= 0)
558                     return (char *) 0;
559 
560           --bsize;  /* save room for null */
561           do {
562                     if (shf->rnleft == 0) {
563                               if (shf_fillbuf(shf) == EOF)
564                                         return NULL;
565                               if (shf->rnleft == 0) {
566                                         *buf = '\0';
567                                         return buf == orig_buf ? NULL : buf;
568                               }
569                     }
570                     end = (unsigned char *) memchr((char *) shf->rp, '\n',
571                                                        shf->rnleft);
572                     ncopy = end ? end - shf->rp + 1 : shf->rnleft;
573                     if (ncopy > bsize)
574                               ncopy = bsize;
575                     memcpy(buf, (char *) shf->rp, ncopy);
576                     shf->rp += ncopy;
577                     shf->rnleft -= ncopy;
578                     buf += ncopy;
579                     bsize -= ncopy;
580 
581           } while (!end && bsize);
582           *buf = '\0';
583           return buf;
584 }
585 
586 /* Returns the char read.  Returns EOF for error and end of file. */
587 int
shf_getchar(shf)588 shf_getchar(shf)
589           struct shf *shf;
590 {
591           if (!(shf->flags & SHF_RD))
592                     internal_errorf(1, "shf_getchar: flags %x", shf->flags);
593 
594           if (shf->rnleft == 0 && (shf_fillbuf(shf) == EOF || shf->rnleft == 0))
595                     return EOF;
596           --shf->rnleft;
597           return *shf->rp++;
598 }
599 
600 /* Put a character back in the input stream.  Returns the character if
601  * successful, EOF if there is no room.
602  */
603 int
shf_ungetc(c,shf)604 shf_ungetc(c, shf)
605           int c;
606           struct shf *shf;
607 {
608           if (!(shf->flags & SHF_RD))
609                     internal_errorf(1, "shf_ungetc: flags %x", shf->flags);
610 
611           if ((shf->flags & SHF_ERROR) || c == EOF
612               || (shf->rp == shf->buf && shf->rnleft))
613                     return EOF;
614 
615           if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF)
616                     return EOF;
617 
618           if (shf->rp == shf->buf)
619                     shf->rp = shf->buf + shf->rbsize;
620           if (shf->flags & SHF_STRING) {
621                     /* Can unget what was read, but not something different - we
622                      * don't want to modify a string.
623                      */
624                     if (shf->rp[-1] != c)
625                               return EOF;
626                     shf->flags &= ~SHF_EOF;
627                     shf->rp--;
628                     shf->rnleft++;
629                     return c;
630           }
631           shf->flags &= ~SHF_EOF;
632           *--(shf->rp) = c;
633           shf->rnleft++;
634           return c;
635 }
636 
637 /* Write a character.  Returns the character if successful, EOF if
638  * the char could not be written.
639  */
640 int
shf_putchar(c,shf)641 shf_putchar(c, shf)
642           int c;
643           struct shf *shf;
644 {
645           if (!(shf->flags & SHF_WR))
646                     internal_errorf(1, "shf_putchar: flags %x", shf->flags);
647 
648           if (c == EOF)
649                     return EOF;
650 
651           if (shf->flags & SHF_UNBUF) {
652                     char cc = c;
653                     int n;
654 
655                     if (shf->fd < 0)
656                               internal_errorf(1, "shf_putchar: no fd");
657                     if (shf->flags & SHF_ERROR) {
658                               errno = shf->errno_;
659                               return EOF;
660                     }
661                     while ((n = write(shf->fd, &cc, 1)) != 1)
662                               if (n < 0) {
663                                         if (errno == EINTR
664                                             && !(shf->flags & SHF_INTERRUPT))
665                                                   continue;
666                                         shf->flags |= SHF_ERROR;
667                                         shf->errno_ = errno;
668                                         return EOF;
669                               }
670           } else {
671                     /* Flush deals with strings and sticky errors */
672                     if (shf->wnleft == 0 && shf_emptybuf(shf, EB_GROW) == EOF)
673                               return EOF;
674                     shf->wnleft--;
675                     *shf->wp++ = c;
676           }
677 
678           return c;
679 }
680 
681 /* Write a string.  Returns the length of the string if successful, EOF if
682  * the string could not be written.
683  */
684 int
shf_puts(s,shf)685 shf_puts(s, shf)
686           const char *s;
687           struct shf *shf;
688 {
689           if (!s)
690                     return EOF;
691 
692           return shf_write(s, strlen(s), shf);
693 }
694 
695 /* Write a buffer.  Returns nbytes if successful, EOF if there is an error. */
696 int
shf_write(buf,nbytes,shf)697 shf_write(buf, nbytes, shf)
698           const char *buf;
699           int nbytes;
700           struct shf *shf;
701 {
702           int orig_nbytes = nbytes;
703           int n;
704           int ncopy;
705 
706           if (!(shf->flags & SHF_WR))
707                     internal_errorf(1, "shf_write: flags %x", shf->flags);
708 
709           if (nbytes < 0)
710                     internal_errorf(1, "shf_write: nbytes %d", nbytes);
711 
712           /* Don't buffer if buffer is empty and we're writing a large amount. */
713           if ((ncopy = shf->wnleft)
714               && (shf->wp != shf->buf || nbytes < shf->wnleft))
715           {
716                     if (ncopy > nbytes)
717                               ncopy = nbytes;
718                     memcpy(shf->wp, buf, ncopy);
719                     nbytes -= ncopy;
720                     buf += ncopy;
721                     shf->wp += ncopy;
722                     shf->wnleft -= ncopy;
723           }
724           if (nbytes > 0) {
725                     /* Flush deals with strings and sticky errors */
726                     if (shf_emptybuf(shf, EB_GROW) == EOF)
727                               return EOF;
728                     if (nbytes > shf->wbsize) {
729                               ncopy = nbytes;
730                               if (shf->wbsize)
731                                         ncopy -= nbytes % shf->wbsize;
732                               nbytes -= ncopy;
733                               while (ncopy > 0) {
734                                         n = write(shf->fd, buf, ncopy);
735                                         if (n < 0) {
736                                                   if (errno == EINTR
737                                                       && !(shf->flags & SHF_INTERRUPT))
738                                                             continue;
739                                                   shf->flags |= SHF_ERROR;
740                                                   shf->errno_ = errno;
741                                                   shf->wnleft = 0;
742                                                   /* Note: fwrite(3S) returns 0 for
743                                                    * errors - this doesn't */
744                                                   return EOF;
745                                         }
746                                         buf += n;
747                                         ncopy -= n;
748                               }
749                     }
750                     if (nbytes > 0) {
751                               memcpy(shf->wp, buf, nbytes);
752                               shf->wp += nbytes;
753                               shf->wnleft -= nbytes;
754                     }
755           }
756 
757           return orig_nbytes;
758 }
759 
760 int
shf_fprintf(struct shf * shf,const char * fmt,...)761 shf_fprintf(struct shf *shf, const char *fmt, ...)
762 {
763           va_list args;
764           int n;
765 
766           va_start(args, fmt);
767           n = shf_vfprintf(shf, fmt, args);
768           va_end(args);
769 
770           return n;
771 }
772 
773 int
shf_snprintf(char * buf,int bsize,const char * fmt,...)774 shf_snprintf(char *buf, int bsize, const char *fmt, ...)
775 {
776           struct shf shf;
777           va_list args;
778           int n;
779 
780           if (!buf || bsize <= 0)
781                     internal_errorf(1, "shf_snprintf: buf %lx, bsize %d",
782                               (long) buf, bsize);
783 
784           shf_sopen(buf, bsize, SHF_WR, &shf);
785           va_start(args, fmt);
786           n = shf_vfprintf(&shf, fmt, args);
787           va_end(args);
788           shf_sclose(&shf); /* null terminates */
789           return n;
790 }
791 
792 char *
shf_smprintf(const char * fmt,...)793 shf_smprintf(const char *fmt, ...)
794 {
795           struct shf shf;
796           va_list args;
797 
798           shf_sopen((char *) 0, 0, SHF_WR|SHF_DYNAMIC, &shf);
799           va_start(args, fmt);
800           shf_vfprintf(&shf, fmt, args);
801           va_end(args);
802           return shf_sclose(&shf); /* null terminates */
803 }
804 
805 #undef FP                               /* if you want floating point stuff */
806 
807 #define BUF_SIZE    128
808 #define FPBUF_SIZE  (DMAXEXP+16)/* this must be >
809                                          *        MAX(DMAXEXP, log10(pow(2, DSIGNIF)))
810                                          *    + ceil(log10(DMAXEXP)) + 8 (I think).
811                                          * Since this is hard to express as a
812                                          * constant, just use a large buffer.
813                                          */
814 
815 /*
816  *        What kinda of machine we on?  Hopefully the C compiler will optimize
817  *  this out...
818  *
819  *        For shorts, we want sign extend for %d but not for %[oxu] - on 16 bit
820  *  machines it don't matter.  Assumes C compiler has converted shorts to
821  *  ints before pushing them.
822  */
823 #define POP_INT(f, s, a) (((f) & FL_LONG) ?                                     \
824                                         va_arg((a), unsigned long)              \
825                                   :                                                       \
826                                         (sizeof(int) < sizeof(long) ?           \
827                                                   ((s) ?                                  \
828                                                             (long) va_arg((a), int)       \
829                                                       :                                   \
830                                                             va_arg((a), unsigned))        \
831                                             :                                             \
832                                                   va_arg((a), unsigned)))
833 
834 #define ABIGNUM               32000     /* big numer that will fit in a short */
835 #define LOG2_10               3.321928094887362347870319429 /* log base 2 of 10 */
836 
837 #define   FL_HASH             0x001     /* `#' seen */
838 #define FL_PLUS               0x002     /* `+' seen */
839 #define FL_RIGHT    0x004     /* `-' seen */
840 #define FL_BLANK    0x008     /* ` ' seen */
841 #define FL_SHORT    0x010     /* `h' seen */
842 #define FL_LONG               0x020     /* `l' seen */
843 #define FL_ZERO               0x040     /* `0' seen */
844 #define FL_DOT                0x080     /* '.' seen */
845 #define FL_UPPER    0x100     /* format character was uppercase */
846 #define FL_NUMBER   0x200     /* a number was formatted %[douxefg] */
847 
848 
849 #ifdef FP
850 #include <math.h>
851 
852 static double
my_ceil(d)853 my_ceil(d)
854           double    d;
855 {
856           double              i;
857 
858           return d - modf(d, &i) + (d < 0 ? -1 : 1);
859 }
860 #endif /* FP */
861 
862 int
shf_vfprintf(shf,fmt,args)863 shf_vfprintf(shf, fmt, args)
864           struct shf *shf;
865           const char *fmt;
866           va_list args;
867 {
868           char                c, *s;
869           int                 UNINITIALIZED(tmp);
870           int                 field, precision;
871           int                 len;
872           int                 flags;
873           unsigned long       lnum;
874                                                   /* %#o produces the longest output */
875           char                numbuf[(BITS(long) + 2) / 3 + 1];
876           /* this stuff for dealing with the buffer */
877           int                 nwritten = 0;
878           static char nulls[] = "(null %s)";
879 #ifdef FP
880           /* should be in <math.h>
881            *  extern double frexp();
882            */
883           extern char *ecvt();
884 
885           double              fpnum;
886           int                 expo, decpt;
887           char                style;
888           char                fpbuf[FPBUF_SIZE];
889 #endif /* FP */
890 
891           if (!fmt)
892                     return 0;
893 
894           while ((c = *fmt++)) {
895                     if (c != '%') {
896                               shf_putc(c, shf);
897                               nwritten++;
898                               continue;
899                     }
900                     /*
901                      *        This will accept flags/fields in any order - not
902                      *  just the order specified in printf(3), but this is
903                      *  the way _doprnt() seems to work (on bsd and sysV).
904                      *  The only restriction is that the format character must
905                      *  come last :-).
906                      */
907                     flags = field = precision = 0;
908                     for ( ; (c = *fmt++) ; ) {
909                               switch (c) {
910                               case '#':
911                                         flags |= FL_HASH;
912                                         continue;
913 
914                               case '+':
915                                         flags |= FL_PLUS;
916                                         continue;
917 
918                               case '-':
919                                         flags |= FL_RIGHT;
920                                         continue;
921 
922                               case ' ':
923                                         flags |= FL_BLANK;
924                                         continue;
925 
926                               case '0':
927                                         if (!(flags & FL_DOT))
928                                                   flags |= FL_ZERO;
929                                         continue;
930 
931                               case '.':
932                                         flags |= FL_DOT;
933                                         precision = 0;
934                                         continue;
935 
936                               case '*':
937                                         tmp = va_arg(args, int);
938                                         if (flags & FL_DOT)
939                                                   precision = tmp;
940                                         else if ((field = tmp) < 0) {
941                                                   field = -field;
942                                                   flags |= FL_RIGHT;
943                                         }
944                                         continue;
945 
946                               case 'l':
947                                         flags |= FL_LONG;
948                                         continue;
949 
950                               case 'h':
951                                         flags |= FL_SHORT;
952                                         continue;
953                               }
954                               if (digit(c)) {
955                                         tmp = c - '0';
956                                         while (c = *fmt++, digit(c))
957                                                   tmp = tmp * 10 + c - '0';
958                                         --fmt;
959                                         if (tmp < 0)                  /* overflow? */
960                                                   tmp = 0;
961                                         if (flags & FL_DOT)
962                                                   precision = tmp;
963                                         else
964                                                   field = tmp;
965                                         continue;
966                               }
967                               break;
968                     }
969 
970                     if (precision < 0)
971                               precision = 0;
972 
973                     if (!c)             /* nasty format */
974                               break;
975 
976                     if (c >= 'A' && c <= 'Z') {
977                               flags |= FL_UPPER;
978                               c = c - 'A' + 'a';
979                     }
980 
981                     switch (c) {
982                     case 'p': /* pointer */
983                               flags &= ~(FL_LONG | FL_SHORT);
984                               if (sizeof(char *) > sizeof(int))
985                                         flags |= FL_LONG; /* hope it fits.. */
986                               /* aaahhh... */
987                               /*FALLTHROUGH*/
988                     case 'd':
989                     case 'i':
990                     case 'o':
991                     case 'u':
992                     case 'x':
993                               flags |= FL_NUMBER;
994                               s = &numbuf[sizeof(numbuf)];
995                               lnum = POP_INT(flags, c == 'd', args);
996                               switch (c) {
997                               case 'd':
998                               case 'i':
999                                         if (0 > (long) lnum)
1000                                                   lnum = - (long) lnum, tmp = 1;
1001                                         else
1002                                                   tmp = 0;
1003                                         /* aaahhhh..... */
1004                                         /*FALLTHROUGH*/
1005                               case 'u':
1006                                         do {
1007                                                   *--s = lnum % 10 + '0';
1008                                                   lnum /= 10;
1009                                         } while (lnum);
1010 
1011                                         if (c != 'u') {
1012                                                   if (tmp)
1013                                                             *--s = '-';
1014                                                   else if (flags & FL_PLUS)
1015                                                             *--s = '+';
1016                                                   else if (flags & FL_BLANK)
1017                                                             *--s = ' ';
1018                                         }
1019                                         break;
1020 
1021                               case 'o':
1022                                         do {
1023                                                   *--s = (lnum & 0x7) + '0';
1024                                                   lnum >>= 3;
1025                                         } while (lnum);
1026 
1027                                         if ((flags & FL_HASH) && *s != '0')
1028                                                   *--s = '0';
1029                                         break;
1030 
1031                               case 'p':
1032                               case 'x':
1033                                   {
1034                                         const char *digits = (flags & FL_UPPER) ?
1035                                                               "0123456789ABCDEF"
1036                                                             : "0123456789abcdef";
1037                                         do {
1038                                                   *--s = digits[lnum & 0xf];
1039                                                   lnum >>= 4;
1040                                         } while (lnum);
1041 
1042                                         if (flags & FL_HASH) {
1043                                                   *--s = (flags & FL_UPPER) ? 'X' : 'x';
1044                                                   *--s = '0';
1045                                         }
1046                                   }
1047                               }
1048                               len = &numbuf[sizeof(numbuf)] - s;
1049                               if (flags & FL_DOT) {
1050                                         if (precision > len) {
1051                                                   field = precision;
1052                                                   flags |= FL_ZERO;
1053                                         } else
1054                                                   precision = len; /* no loss */
1055                               }
1056                               break;
1057 
1058 #ifdef FP
1059                     case 'e':
1060                     case 'g':
1061                     case 'f':
1062                         {
1063                               char *p;
1064 
1065                               /*
1066                                *        This could probably be done better,
1067                                *  but it seems to work.  Note that gcvt()
1068                                *  is not used, as you cannot tell it to
1069                                *  not strip the zeros.
1070                                */
1071                               flags |= FL_NUMBER;
1072                               if (!(flags & FL_DOT))
1073                                         precision = 6;      /* default */
1074                               /*
1075                                *        Assumes doubles are pushed on
1076                                *  the stack.  If this is not so, then
1077                                *  FL_LONG/FL_SHORT should be checked.
1078                                */
1079                               fpnum = va_arg(args, double);
1080                               s = fpbuf;
1081                               style = c;
1082                               /*
1083                                *  This is the same as
1084                                *        expo = ceil(log10(fpnum))
1085                                *  but doesn't need -lm.  This is an
1086                                *  approximation as expo is rounded up.
1087                                */
1088                               (void) frexp(fpnum, &expo);
1089                               expo = my_ceil(expo / LOG2_10);
1090 
1091                               if (expo < 0)
1092                                         expo = 0;
1093 
1094                               p = ecvt(fpnum, precision + 1 + expo,
1095                                          &decpt, &tmp);
1096                               if (c == 'g') {
1097                                         if (decpt < -4 || decpt > precision)
1098                                                   style = 'e';
1099                                         else
1100                                                   style = 'f';
1101                                         if (decpt > 0 && (precision -= decpt) < 0)
1102                                                   precision = 0;
1103                               }
1104                               if (tmp)
1105                                         *s++ = '-';
1106                               else if (flags & FL_PLUS)
1107                                         *s++ = '+';
1108                               else if (flags & FL_BLANK)
1109                                         *s++ = ' ';
1110 
1111                               if (style == 'e')
1112                                         *s++ = *p++;
1113                               else {
1114                                         if (decpt > 0) {
1115                                                   /* Overflow check - should
1116                                                    * never have this problem.
1117                                                    */
1118                                                   if (decpt >
1119                                                             &fpbuf[sizeof(fpbuf)]
1120                                                                       - s - 8)
1121                                                             decpt =
1122                                                              &fpbuf[sizeof(fpbuf)]
1123                                                                       - s - 8;
1124                                                   (void) memcpy(s, p, decpt);
1125                                                   s += decpt;
1126                                                   p += decpt;
1127                                         } else
1128                                                   *s++ = '0';
1129                               }
1130 
1131                               /* print the fraction? */
1132                               if (precision > 0) {
1133                                         *s++ = '.';
1134                                         /* Overflow check - should
1135                                          * never have this problem.
1136                                          */
1137                                         if (precision > &fpbuf[sizeof(fpbuf)]
1138                                                                       - s - 7)
1139                                                   precision =
1140                                                             &fpbuf[sizeof(fpbuf)]
1141                                                             - s - 7;
1142                                         for (tmp = decpt;  tmp++ < 0 &&
1143                                                       precision > 0 ; precision--)
1144                                                   *s++ = '0';
1145                                         tmp = strlen(p);
1146                                         if (precision > tmp)
1147                                                   precision = tmp;
1148                                         /* Overflow check - should
1149                                          * never have this problem.
1150                                          */
1151                                         if (precision > &fpbuf[sizeof(fpbuf)]
1152                                                                       - s - 7)
1153                                                   precision =
1154                                                             &fpbuf[sizeof(fpbuf)]
1155                                                             - s - 7;
1156                                         (void) memcpy(s, p, precision);
1157                                         s += precision;
1158                                         /*
1159                                          *        `g' format strips trailing
1160                                          *  zeros after the decimal.
1161                                          */
1162                                         if (c == 'g' && !(flags & FL_HASH)) {
1163                                                   while (*--s == '0')
1164                                                             ;
1165                                                   if (*s != '.')
1166                                                             s++;
1167                                         }
1168                               } else if (flags & FL_HASH)
1169                                         *s++ = '.';
1170 
1171                               if (style == 'e') {
1172                                         *s++ = (flags & FL_UPPER) ? 'E' : 'e';
1173                                         if (--decpt >= 0)
1174                                                   *s++ = '+';
1175                                         else {
1176                                                   *s++ = '-';
1177                                                   decpt = -decpt;
1178                                         }
1179                                         p = &numbuf[sizeof(numbuf)];
1180                                         for (tmp = 0; tmp < 2 || decpt ; tmp++) {
1181                                                   *--p = '0' + decpt % 10;
1182                                                   decpt /= 10;
1183                                         }
1184                                         tmp = &numbuf[sizeof(numbuf)] - p;
1185                                         (void) memcpy(s, p, tmp);
1186                                         s += tmp;
1187                               }
1188 
1189                               len = s - fpbuf;
1190                               s = fpbuf;
1191                               precision = len;
1192                               break;
1193                         }
1194 #endif /* FP */
1195 
1196                     case 's':
1197                               if (!(s = va_arg(args, char *)))
1198                                         s = nulls;
1199                               len = strlen(s);
1200                               break;
1201 
1202                     case 'c':
1203                               flags &= ~FL_DOT;
1204                               numbuf[0] = va_arg(args, int);
1205                               s = numbuf;
1206                               len = 1;
1207                               break;
1208 
1209                     case '%':
1210                     default:
1211                               numbuf[0] = c;
1212                               s = numbuf;
1213                               len = 1;
1214                               break;
1215                     }
1216 
1217                     /*
1218                      *        At this point s should point to a string that is
1219                      *  to be formatted, and len should be the length of the
1220                      *  string.
1221                      */
1222                     if (!(flags & FL_DOT) || len < precision)
1223                               precision = len;
1224                     if (field > precision) {
1225                               field -= precision;
1226                               if (!(flags & FL_RIGHT)) {
1227                                         field = -field;
1228                                         /* skip past sign or 0x when padding with 0 */
1229                                         if ((flags & FL_ZERO) && (flags & FL_NUMBER)) {
1230                                                   if (*s == '+' || *s == '-' || *s ==' ')
1231                                                   {
1232                                                             shf_putc(*s, shf);
1233                                                             s++;
1234                                                             precision--;
1235                                                             nwritten++;
1236                                                   } else if (*s == '0') {
1237                                                             shf_putc(*s, shf);
1238                                                             s++;
1239                                                             nwritten++;
1240                                                             if (--precision > 0 &&
1241                                                                       (*s | 0x20) == 'x')
1242                                                             {
1243                                                                       shf_putc(*s, shf);
1244                                                                       s++;
1245                                                                       precision--;
1246                                                                       nwritten++;
1247                                                             }
1248                                                   }
1249                                                   c = '0';
1250                                         } else
1251                                                   c = flags & FL_ZERO ? '0' : ' ';
1252                                         if (field < 0) {
1253                                                   nwritten += -field;
1254                                                   for ( ; field < 0 ; field++)
1255                                                             shf_putc(c, shf);
1256                                         }
1257                               } else
1258                                         c = ' ';
1259                     } else
1260                               field = 0;
1261 
1262                     if (precision > 0) {
1263                               nwritten += precision;
1264                               for ( ; precision-- > 0 ; s++)
1265                                         shf_putc(*s, shf);
1266                     }
1267                     if (field > 0) {
1268                               nwritten += field;
1269                               for ( ; field > 0 ; --field)
1270                                         shf_putc(c, shf);
1271                     }
1272           }
1273 
1274           return shf_error(shf) ? EOF : nwritten;
1275 }
1276