OSDN Git Service

096275293f9c20aff13804ff934d461e16b5d4fd
[android-x86/external-mksh.git] / src / shf.c
1 /*      $OpenBSD: shf.c,v 1.15 2006/04/02 00:48:33 deraadt Exp $        */
2
3 /*-
4  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009
5  *      Thorsten Glaser <tg@mirbsd.org>
6  *
7  * Provided that these terms and disclaimer and all copyright notices
8  * are retained or reproduced in an accompanying document, permission
9  * is granted to deal in this work without restriction, including un-
10  * limited rights to use, publicly perform, distribute, sell, modify,
11  * merge, give away, or sublicence.
12  *
13  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
14  * the utmost extent permitted by applicable law, neither express nor
15  * implied; without malicious intent or gross negligence. In no event
16  * may a licensor, author or contributor be held liable for indirect,
17  * direct, other damage, loss, or other issues arising in any way out
18  * of dealing in the work, even if advised of the possibility of such
19  * damage or existence of a defect, except proven that it results out
20  * of said person's immediate fault when using the work as intended.
21  */
22
23 #include "sh.h"
24
25 __RCSID("$MirOS: src/bin/mksh/shf.c,v 1.36 2010/07/19 22:41:04 tg Exp $");
26
27 /* flags to shf_emptybuf() */
28 #define EB_READSW       0x01    /* about to switch to reading */
29 #define EB_GROW         0x02    /* grow buffer if necessary (STRING+DYNAMIC) */
30
31 /*
32  * Replacement stdio routines. Stdio is too flakey on too many machines
33  * to be useful when you have multiple processes using the same underlying
34  * file descriptors.
35  */
36
37 static int shf_fillbuf(struct shf *);
38 static int shf_emptybuf(struct shf *, int);
39
40 /* Open a file. First three args are for open(), last arg is flags for
41  * this package. Returns NULL if file could not be opened, or if a dup
42  * fails.
43  */
44 struct shf *
45 shf_open(const char *name, int oflags, int mode, int sflags)
46 {
47         struct shf *shf;
48         int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
49         int fd;
50
51         /* Done before open so if alloca fails, fd won't be lost. */
52         shf = alloc(sizeof(struct shf) + bsize, ATEMP);
53         shf->areap = ATEMP;
54         shf->buf = (unsigned char *)&shf[1];
55         shf->bsize = bsize;
56         shf->flags = SHF_ALLOCS;
57         /* Rest filled in by reopen. */
58
59         fd = open(name, oflags, mode);
60         if (fd < 0) {
61                 afree(shf, shf->areap);
62                 return (NULL);
63         }
64         if ((sflags & SHF_MAPHI) && fd < FDBASE) {
65                 int nfd;
66
67                 nfd = fcntl(fd, F_DUPFD, FDBASE);
68                 close(fd);
69                 if (nfd < 0) {
70                         afree(shf, shf->areap);
71                         return (NULL);
72                 }
73                 fd = nfd;
74         }
75         sflags &= ~SHF_ACCMODE;
76         sflags |= (oflags & O_ACCMODE) == O_RDONLY ? SHF_RD :
77             ((oflags & O_ACCMODE) == O_WRONLY ? SHF_WR : SHF_RDWR);
78
79         return (shf_reopen(fd, sflags, shf));
80 }
81
82 /* Set up the shf structure for a file descriptor. Doesn't fail. */
83 struct shf *
84 shf_fdopen(int fd, int sflags, struct shf *shf)
85 {
86         int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
87
88         /* use fcntl() to figure out correct read/write flags */
89         if (sflags & SHF_GETFL) {
90                 int flags = fcntl(fd, F_GETFL, 0);
91
92                 if (flags < 0)
93                         /* will get an error on first read/write */
94                         sflags |= SHF_RDWR;
95                 else {
96                         switch (flags & O_ACCMODE) {
97                         case O_RDONLY:
98                                 sflags |= SHF_RD;
99                                 break;
100                         case O_WRONLY:
101                                 sflags |= SHF_WR;
102                                 break;
103                         case O_RDWR:
104                                 sflags |= SHF_RDWR;
105                                 break;
106                         }
107                 }
108         }
109
110         if (!(sflags & (SHF_RD | SHF_WR)))
111                 internal_errorf("shf_fdopen: missing read/write");
112
113         if (shf) {
114                 if (bsize) {
115                         shf->buf = alloc(bsize, ATEMP);
116                         sflags |= SHF_ALLOCB;
117                 } else
118                         shf->buf = NULL;
119         } else {
120                 shf = alloc(sizeof(struct shf) + bsize, ATEMP);
121                 shf->buf = (unsigned char *)&shf[1];
122                 sflags |= SHF_ALLOCS;
123         }
124         shf->areap = ATEMP;
125         shf->fd = fd;
126         shf->rp = shf->wp = shf->buf;
127         shf->rnleft = 0;
128         shf->rbsize = bsize;
129         shf->wnleft = 0; /* force call to shf_emptybuf() */
130         shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;
131         shf->flags = sflags;
132         shf->errno_ = 0;
133         shf->bsize = bsize;
134         if (sflags & SHF_CLEXEC)
135                 fcntl(fd, F_SETFD, FD_CLOEXEC);
136         return (shf);
137 }
138
139 /* Set up an existing shf (and buffer) to use the given fd */
140 struct shf *
141 shf_reopen(int fd, int sflags, struct shf *shf)
142 {
143         int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
144
145         /* use fcntl() to figure out correct read/write flags */
146         if (sflags & SHF_GETFL) {
147                 int flags = fcntl(fd, F_GETFL, 0);
148
149                 if (flags < 0)
150                         /* will get an error on first read/write */
151                         sflags |= SHF_RDWR;
152                 else {
153                         switch (flags & O_ACCMODE) {
154                         case O_RDONLY:
155                                 sflags |= SHF_RD;
156                                 break;
157                         case O_WRONLY:
158                                 sflags |= SHF_WR;
159                                 break;
160                         case O_RDWR:
161                                 sflags |= SHF_RDWR;
162                                 break;
163                         }
164                 }
165         }
166
167         if (!(sflags & (SHF_RD | SHF_WR)))
168                 internal_errorf("shf_reopen: missing read/write");
169         if (!shf || !shf->buf || shf->bsize < bsize)
170                 internal_errorf("shf_reopen: bad shf/buf/bsize");
171
172         /* assumes shf->buf and shf->bsize already set up */
173         shf->fd = fd;
174         shf->rp = shf->wp = shf->buf;
175         shf->rnleft = 0;
176         shf->rbsize = bsize;
177         shf->wnleft = 0; /* force call to shf_emptybuf() */
178         shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;
179         shf->flags = (shf->flags & (SHF_ALLOCS | SHF_ALLOCB)) | sflags;
180         shf->errno_ = 0;
181         if (sflags & SHF_CLEXEC)
182                 fcntl(fd, F_SETFD, FD_CLOEXEC);
183         return (shf);
184 }
185
186 /* Open a string for reading or writing. If reading, bsize is the number
187  * of bytes that can be read. If writing, bsize is the maximum number of
188  * bytes that can be written. If shf is not null, it is filled in and
189  * returned, if it is null, shf is allocated. If writing and buf is null
190  * and SHF_DYNAMIC is set, the buffer is allocated (if bsize > 0, it is
191  * used for the initial size). Doesn't fail.
192  * When writing, a byte is reserved for a trailing null - see shf_sclose().
193  */
194 struct shf *
195 shf_sopen(char *buf, int bsize, int sflags, struct shf *shf)
196 {
197         /* can't have a read+write string */
198         if (!(!(sflags & SHF_RD) ^ !(sflags & SHF_WR)))
199                 internal_errorf("shf_sopen: flags 0x%x", sflags);
200
201         if (!shf) {
202                 shf = alloc(sizeof(struct shf), ATEMP);
203                 sflags |= SHF_ALLOCS;
204         }
205         shf->areap = ATEMP;
206         if (!buf && (sflags & SHF_WR) && (sflags & SHF_DYNAMIC)) {
207                 if (bsize <= 0)
208                         bsize = 64;
209                 sflags |= SHF_ALLOCB;
210                 buf = alloc(bsize, shf->areap);
211         }
212         shf->fd = -1;
213         shf->buf = shf->rp = shf->wp = (unsigned char *)buf;
214         shf->rnleft = bsize;
215         shf->rbsize = bsize;
216         shf->wnleft = bsize - 1;        /* space for a '\0' */
217         shf->wbsize = bsize;
218         shf->flags = sflags | SHF_STRING;
219         shf->errno_ = 0;
220         shf->bsize = bsize;
221
222         return (shf);
223 }
224
225 /* Flush and close file descriptor, free the shf structure */
226 int
227 shf_close(struct shf *shf)
228 {
229         int ret = 0;
230
231         if (shf->fd >= 0) {
232                 ret = shf_flush(shf);
233                 if (close(shf->fd) < 0)
234                         ret = EOF;
235         }
236         if (shf->flags & SHF_ALLOCS)
237                 afree(shf, shf->areap);
238         else if (shf->flags & SHF_ALLOCB)
239                 afree(shf->buf, shf->areap);
240
241         return (ret);
242 }
243
244 /* Flush and close file descriptor, don't free file structure */
245 int
246 shf_fdclose(struct shf *shf)
247 {
248         int ret = 0;
249
250         if (shf->fd >= 0) {
251                 ret = shf_flush(shf);
252                 if (close(shf->fd) < 0)
253                         ret = EOF;
254                 shf->rnleft = 0;
255                 shf->rp = shf->buf;
256                 shf->wnleft = 0;
257                 shf->fd = -1;
258         }
259
260         return (ret);
261 }
262
263 /* Close a string - if it was opened for writing, it is null terminated;
264  * returns a pointer to the string and frees shf if it was allocated
265  * (does not free string if it was allocated).
266  */
267 char *
268 shf_sclose(struct shf *shf)
269 {
270         unsigned char *s = shf->buf;
271
272         /* null terminate */
273         if (shf->flags & SHF_WR) {
274                 shf->wnleft++;
275                 shf_putc('\0', shf);
276         }
277         if (shf->flags & SHF_ALLOCS)
278                 afree(shf, shf->areap);
279         return ((char *)s);
280 }
281
282 /* Un-read what has been read but not examined, or write what has been
283  * buffered. Returns 0 for success, EOF for (write) error.
284  */
285 int
286 shf_flush(struct shf *shf)
287 {
288         if (shf->flags & SHF_STRING)
289                 return ((shf->flags & SHF_WR) ? EOF : 0);
290
291         if (shf->fd < 0)
292                 internal_errorf("shf_flush: no fd");
293
294         if (shf->flags & SHF_ERROR) {
295                 errno = shf->errno_;
296                 return (EOF);
297         }
298
299         if (shf->flags & SHF_READING) {
300                 shf->flags &= ~(SHF_EOF | SHF_READING);
301                 if (shf->rnleft > 0) {
302                         lseek(shf->fd, (off_t)-shf->rnleft, SEEK_CUR);
303                         shf->rnleft = 0;
304                         shf->rp = shf->buf;
305                 }
306                 return (0);
307         } else if (shf->flags & SHF_WRITING)
308                 return (shf_emptybuf(shf, 0));
309
310         return (0);
311 }
312
313 /* Write out any buffered data. If currently reading, flushes the read
314  * buffer. Returns 0 for success, EOF for (write) error.
315  */
316 static int
317 shf_emptybuf(struct shf *shf, int flags)
318 {
319         int ret = 0;
320
321         if (!(shf->flags & SHF_STRING) && shf->fd < 0)
322                 internal_errorf("shf_emptybuf: no fd");
323
324         if (shf->flags & SHF_ERROR) {
325                 errno = shf->errno_;
326                 return (EOF);
327         }
328
329         if (shf->flags & SHF_READING) {
330                 if (flags & EB_READSW) /* doesn't happen */
331                         return (0);
332                 ret = shf_flush(shf);
333                 shf->flags &= ~SHF_READING;
334         }
335         if (shf->flags & SHF_STRING) {
336                 unsigned char *nbuf;
337
338                 /* Note that we assume SHF_ALLOCS is not set if SHF_ALLOCB
339                  * is set... (changing the shf pointer could cause problems)
340                  */
341                 if (!(flags & EB_GROW) || !(shf->flags & SHF_DYNAMIC) ||
342                     !(shf->flags & SHF_ALLOCB))
343                         return (EOF);
344                 /* allocate more space for buffer */
345                 nbuf = aresize(shf->buf, 2 * shf->wbsize, shf->areap);
346                 shf->rp = nbuf + (shf->rp - shf->buf);
347                 shf->wp = nbuf + (shf->wp - shf->buf);
348                 shf->rbsize += shf->wbsize;
349                 shf->wnleft += shf->wbsize;
350                 shf->wbsize *= 2;
351                 shf->buf = nbuf;
352         } else {
353                 if (shf->flags & SHF_WRITING) {
354                         int ntowrite = shf->wp - shf->buf;
355                         unsigned char *buf = shf->buf;
356                         int n;
357
358                         while (ntowrite > 0) {
359                                 n = write(shf->fd, buf, ntowrite);
360                                 if (n < 0) {
361                                         if (errno == EINTR &&
362                                             !(shf->flags & SHF_INTERRUPT))
363                                                 continue;
364                                         shf->flags |= SHF_ERROR;
365                                         shf->errno_ = errno;
366                                         shf->wnleft = 0;
367                                         if (buf != shf->buf) {
368                                                 /* allow a second flush
369                                                  * to work */
370                                                 memmove(shf->buf, buf,
371                                                     ntowrite);
372                                                 shf->wp = shf->buf + ntowrite;
373                                         }
374                                         return (EOF);
375                                 }
376                                 buf += n;
377                                 ntowrite -= n;
378                         }
379                         if (flags & EB_READSW) {
380                                 shf->wp = shf->buf;
381                                 shf->wnleft = 0;
382                                 shf->flags &= ~SHF_WRITING;
383                                 return (0);
384                         }
385                 }
386                 shf->wp = shf->buf;
387                 shf->wnleft = shf->wbsize;
388         }
389         shf->flags |= SHF_WRITING;
390
391         return (ret);
392 }
393
394 /* Fill up a read buffer. Returns EOF for a read error, 0 otherwise. */
395 static int
396 shf_fillbuf(struct shf *shf)
397 {
398         if (shf->flags & SHF_STRING)
399                 return (0);
400
401         if (shf->fd < 0)
402                 internal_errorf("shf_fillbuf: no fd");
403
404         if (shf->flags & (SHF_EOF | SHF_ERROR)) {
405                 if (shf->flags & SHF_ERROR)
406                         errno = shf->errno_;
407                 return (EOF);
408         }
409
410         if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF)
411                 return (EOF);
412
413         shf->flags |= SHF_READING;
414
415         shf->rp = shf->buf;
416         while (1) {
417                 shf->rnleft = blocking_read(shf->fd, (char *) shf->buf,
418                     shf->rbsize);
419                 if (shf->rnleft < 0 && errno == EINTR &&
420                     !(shf->flags & SHF_INTERRUPT))
421                         continue;
422                 break;
423         }
424         if (shf->rnleft <= 0) {
425                 if (shf->rnleft < 0) {
426                         shf->flags |= SHF_ERROR;
427                         shf->errno_ = errno;
428                         shf->rnleft = 0;
429                         shf->rp = shf->buf;
430                         return (EOF);
431                 }
432                 shf->flags |= SHF_EOF;
433         }
434         return (0);
435 }
436
437 /* Read a buffer from shf. Returns the number of bytes read into buf,
438  * if no bytes were read, returns 0 if end of file was seen, EOF if
439  * a read error occurred.
440  */
441 int
442 shf_read(char *buf, int bsize, struct shf *shf)
443 {
444         int orig_bsize = bsize;
445         int ncopy;
446
447         if (!(shf->flags & SHF_RD))
448                 internal_errorf("shf_read: flags %x", shf->flags);
449
450         if (bsize <= 0)
451                 internal_errorf("shf_read: bsize %d", bsize);
452
453         while (bsize > 0) {
454                 if (shf->rnleft == 0 &&
455                     (shf_fillbuf(shf) == EOF || shf->rnleft == 0))
456                         break;
457                 ncopy = shf->rnleft;
458                 if (ncopy > bsize)
459                         ncopy = bsize;
460                 memcpy(buf, shf->rp, ncopy);
461                 buf += ncopy;
462                 bsize -= ncopy;
463                 shf->rp += ncopy;
464                 shf->rnleft -= ncopy;
465         }
466         /* Note: fread(3S) returns 0 for errors - this doesn't */
467         return (orig_bsize == bsize ? (shf_error(shf) ? EOF : 0) :
468             orig_bsize - bsize);
469 }
470
471 /* Read up to a newline or EOF. The newline is put in buf; buf is always
472  * null terminated. Returns NULL on read error or if nothing was read before
473  * end of file, returns a pointer to the null byte in buf otherwise.
474  */
475 char *
476 shf_getse(char *buf, int bsize, struct shf *shf)
477 {
478         unsigned char *end;
479         int ncopy;
480         char *orig_buf = buf;
481
482         if (!(shf->flags & SHF_RD))
483                 internal_errorf("shf_getse: flags %x", shf->flags);
484
485         if (bsize <= 0)
486                 return (NULL);
487
488         --bsize;        /* save room for null */
489         do {
490                 if (shf->rnleft == 0) {
491                         if (shf_fillbuf(shf) == EOF)
492                                 return (NULL);
493                         if (shf->rnleft == 0) {
494                                 *buf = '\0';
495                                 return (buf == orig_buf ? NULL : buf);
496                         }
497                 }
498                 end = (unsigned char *)memchr((char *) shf->rp, '\n',
499                     shf->rnleft);
500                 ncopy = end ? end - shf->rp + 1 : shf->rnleft;
501                 if (ncopy > bsize)
502                         ncopy = bsize;
503                 memcpy(buf, (char *) shf->rp, ncopy);
504                 shf->rp += ncopy;
505                 shf->rnleft -= ncopy;
506                 buf += ncopy;
507                 bsize -= ncopy;
508         } while (!end && bsize);
509         *buf = '\0';
510         return (buf);
511 }
512
513 /* Returns the char read. Returns EOF for error and end of file. */
514 int
515 shf_getchar(struct shf *shf)
516 {
517         if (!(shf->flags & SHF_RD))
518                 internal_errorf("shf_getchar: flags %x", shf->flags);
519
520         if (shf->rnleft == 0 && (shf_fillbuf(shf) == EOF || shf->rnleft == 0))
521                 return (EOF);
522         --shf->rnleft;
523         return (*shf->rp++);
524 }
525
526 /* Put a character back in the input stream. Returns the character if
527  * successful, EOF if there is no room.
528  */
529 int
530 shf_ungetc(int c, struct shf *shf)
531 {
532         if (!(shf->flags & SHF_RD))
533                 internal_errorf("shf_ungetc: flags %x", shf->flags);
534
535         if ((shf->flags & SHF_ERROR) || c == EOF ||
536             (shf->rp == shf->buf && shf->rnleft))
537                 return (EOF);
538
539         if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF)
540                 return (EOF);
541
542         if (shf->rp == shf->buf)
543                 shf->rp = shf->buf + shf->rbsize;
544         if (shf->flags & SHF_STRING) {
545                 /* Can unget what was read, but not something different - we
546                  * don't want to modify a string.
547                  */
548                 if (shf->rp[-1] != c)
549                         return (EOF);
550                 shf->flags &= ~SHF_EOF;
551                 shf->rp--;
552                 shf->rnleft++;
553                 return (c);
554         }
555         shf->flags &= ~SHF_EOF;
556         *--(shf->rp) = c;
557         shf->rnleft++;
558         return (c);
559 }
560
561 /* Write a character. Returns the character if successful, EOF if
562  * the char could not be written.
563  */
564 int
565 shf_putchar(int c, struct shf *shf)
566 {
567         if (!(shf->flags & SHF_WR))
568                 internal_errorf("shf_putchar: flags %x", shf->flags);
569
570         if (c == EOF)
571                 return (EOF);
572
573         if (shf->flags & SHF_UNBUF) {
574                 unsigned char cc = (unsigned char)c;
575                 int n;
576
577                 if (shf->fd < 0)
578                         internal_errorf("shf_putchar: no fd");
579                 if (shf->flags & SHF_ERROR) {
580                         errno = shf->errno_;
581                         return (EOF);
582                 }
583                 while ((n = write(shf->fd, &cc, 1)) != 1)
584                         if (n < 0) {
585                                 if (errno == EINTR &&
586                                     !(shf->flags & SHF_INTERRUPT))
587                                         continue;
588                                 shf->flags |= SHF_ERROR;
589                                 shf->errno_ = errno;
590                                 return (EOF);
591                         }
592         } else {
593                 /* Flush deals with strings and sticky errors */
594                 if (shf->wnleft == 0 && shf_emptybuf(shf, EB_GROW) == EOF)
595                         return (EOF);
596                 shf->wnleft--;
597                 *shf->wp++ = c;
598         }
599
600         return (c);
601 }
602
603 /* Write a string. Returns the length of the string if successful, EOF if
604  * the string could not be written.
605  */
606 int
607 shf_puts(const char *s, struct shf *shf)
608 {
609         if (!s)
610                 return (EOF);
611
612         return (shf_write(s, strlen(s), shf));
613 }
614
615 /* Write a buffer. Returns nbytes if successful, EOF if there is an error. */
616 int
617 shf_write(const char *buf, int nbytes, struct shf *shf)
618 {
619         int n, ncopy, orig_nbytes = nbytes;
620
621         if (!(shf->flags & SHF_WR))
622                 internal_errorf("shf_write: flags %x", shf->flags);
623
624         if (nbytes < 0)
625                 internal_errorf("shf_write: nbytes %d", nbytes);
626
627         /* Don't buffer if buffer is empty and we're writting a large amount. */
628         if ((ncopy = shf->wnleft) &&
629             (shf->wp != shf->buf || nbytes < shf->wnleft)) {
630                 if (ncopy > nbytes)
631                         ncopy = nbytes;
632                 memcpy(shf->wp, buf, ncopy);
633                 nbytes -= ncopy;
634                 buf += ncopy;
635                 shf->wp += ncopy;
636                 shf->wnleft -= ncopy;
637         }
638         if (nbytes > 0) {
639                 if (shf->flags & SHF_STRING) {
640                         /* resize buffer until there's enough space left */
641                         while (nbytes > shf->wnleft)
642                                 if (shf_emptybuf(shf, EB_GROW) == EOF)
643                                         return (EOF);
644                         /* then write everything into the buffer */
645                 } else {
646                         /* flush deals with sticky errors */
647                         if (shf_emptybuf(shf, EB_GROW) == EOF)
648                                 return (EOF);
649                         /* write chunks larger than window size directly */
650                         if (nbytes > shf->wbsize) {
651                                 ncopy = nbytes;
652                                 if (shf->wbsize)
653                                         ncopy -= nbytes % shf->wbsize;
654                                 nbytes -= ncopy;
655                                 while (ncopy > 0) {
656                                         n = write(shf->fd, buf, ncopy);
657                                         if (n < 0) {
658                                                 if (errno == EINTR &&
659                                                     !(shf->flags & SHF_INTERRUPT))
660                                                         continue;
661                                                 shf->flags |= SHF_ERROR;
662                                                 shf->errno_ = errno;
663                                                 shf->wnleft = 0;
664                                                 /*
665                                                  * Note: fwrite(3) returns 0
666                                                  * for errors - this doesn't
667                                                  */
668                                                 return (EOF);
669                                         }
670                                         buf += n;
671                                         ncopy -= n;
672                                 }
673                         }
674                         /* ... and buffer the rest */
675                 }
676                 if (nbytes > 0) {
677                         /* write remaining bytes to buffer */
678                         memcpy(shf->wp, buf, nbytes);
679                         shf->wp += nbytes;
680                         shf->wnleft -= nbytes;
681                 }
682         }
683
684         return (orig_nbytes);
685 }
686
687 int
688 shf_fprintf(struct shf *shf, const char *fmt, ...)
689 {
690         va_list args;
691         int n;
692
693         va_start(args, fmt);
694         n = shf_vfprintf(shf, fmt, args);
695         va_end(args);
696
697         return (n);
698 }
699
700 int
701 shf_snprintf(char *buf, int bsize, const char *fmt, ...)
702 {
703         struct shf shf;
704         va_list args;
705         int n;
706
707         if (!buf || bsize <= 0)
708                 internal_errorf("shf_snprintf: buf %p, bsize %d", buf, bsize);
709
710         shf_sopen(buf, bsize, SHF_WR, &shf);
711         va_start(args, fmt);
712         n = shf_vfprintf(&shf, fmt, args);
713         va_end(args);
714         shf_sclose(&shf); /* null terminates */
715         return (n);
716 }
717
718 char *
719 shf_smprintf(const char *fmt, ...)
720 {
721         struct shf shf;
722         va_list args;
723
724         shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
725         va_start(args, fmt);
726         shf_vfprintf(&shf, fmt, args);
727         va_end(args);
728         return (shf_sclose(&shf)); /* null terminates */
729 }
730
731 #undef FP                       /* if you want floating point stuff */
732
733 #ifndef DMAXEXP
734 # define DMAXEXP        128     /* should be big enough */
735 #endif
736
737 #define BUF_SIZE        128
738 /* must be > MAX(DMAXEXP, log10(pow(2, DSIGNIF))) + ceil(log10(DMAXEXP)) + 8
739  * (I think); since it's hard to express as a constant, just use a large buffer
740  */
741 #define FPBUF_SIZE      (DMAXEXP+16)
742
743 #define FL_HASH         0x001   /* '#' seen */
744 #define FL_PLUS         0x002   /* '+' seen */
745 #define FL_RIGHT        0x004   /* '-' seen */
746 #define FL_BLANK        0x008   /* ' ' seen */
747 #define FL_SHORT        0x010   /* 'h' seen */
748 #define FL_LONG         0x020   /* 'l' seen */
749 #define FL_ZERO         0x040   /* '0' seen */
750 #define FL_DOT          0x080   /* '.' seen */
751 #define FL_UPPER        0x100   /* format character was uppercase */
752 #define FL_NUMBER       0x200   /* a number was formated %[douxefg] */
753
754
755 int
756 shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
757 {
758         const char *s;
759         char c, *cp;
760         int tmp = 0, field, precision, len, flags;
761         unsigned long lnum;
762         /* %#o produces the longest output */
763         char numbuf[(8 * sizeof(long) + 2) / 3 + 1];
764         /* this stuff for dealing with the buffer */
765         int nwritten = 0;
766
767         if (!fmt)
768                 return (0);
769
770         while ((c = *fmt++)) {
771                 if (c != '%') {
772                         shf_putc(c, shf);
773                         nwritten++;
774                         continue;
775                 }
776                 /*
777                  * This will accept flags/fields in any order - not
778                  * just the order specified in printf(3), but this is
779                  * the way _doprnt() seems to work (on bsd and sysV).
780                  * The only restriction is that the format character must
781                  * come last :-).
782                  */
783                 flags = field = precision = 0;
784                 for ( ; (c = *fmt++) ; ) {
785                         switch (c) {
786                         case '#':
787                                 flags |= FL_HASH;
788                                 continue;
789
790                         case '+':
791                                 flags |= FL_PLUS;
792                                 continue;
793
794                         case '-':
795                                 flags |= FL_RIGHT;
796                                 continue;
797
798                         case ' ':
799                                 flags |= FL_BLANK;
800                                 continue;
801
802                         case '0':
803                                 if (!(flags & FL_DOT))
804                                         flags |= FL_ZERO;
805                                 continue;
806
807                         case '.':
808                                 flags |= FL_DOT;
809                                 precision = 0;
810                                 continue;
811
812                         case '*':
813                                 tmp = va_arg(args, int);
814                                 if (flags & FL_DOT)
815                                         precision = tmp;
816                                 else if ((field = tmp) < 0) {
817                                         field = -field;
818                                         flags |= FL_RIGHT;
819                                 }
820                                 continue;
821
822                         case 'l':
823                                 flags |= FL_LONG;
824                                 continue;
825
826                         case 'h':
827                                 flags |= FL_SHORT;
828                                 continue;
829                         }
830                         if (ksh_isdigit(c)) {
831                                 tmp = c - '0';
832                                 while (c = *fmt++, ksh_isdigit(c))
833                                         tmp = tmp * 10 + c - '0';
834                                 --fmt;
835                                 if (tmp < 0)            /* overflow? */
836                                         tmp = 0;
837                                 if (flags & FL_DOT)
838                                         precision = tmp;
839                                 else
840                                         field = tmp;
841                                 continue;
842                         }
843                         break;
844                 }
845
846                 if (precision < 0)
847                         precision = 0;
848
849                 if (!c)         /* nasty format */
850                         break;
851
852                 if (c >= 'A' && c <= 'Z') {
853                         flags |= FL_UPPER;
854                         c = ksh_tolower(c);
855                 }
856
857                 switch (c) {
858                 case 'p': /* pointer */
859                         flags &= ~(FL_LONG | FL_SHORT);
860                         flags |= (sizeof(char *) > sizeof(int)) ?
861                             /* hope it fits.. */ FL_LONG : 0;
862                         /* aaahhh... */
863                 case 'd':
864                 case 'i':
865                 case 'o':
866                 case 'u':
867                 case 'x':
868                         flags |= FL_NUMBER;
869                         cp = numbuf + sizeof(numbuf);
870                         /*-
871                          * XXX any better way to do this?
872                          * XXX hopefully the compiler optimises this out
873                          *
874                          * For shorts, we want sign extend for %d but not
875                          * for %[oxu] - on 16 bit machines it doesn't matter.
876                          * Assumes C compiler has converted shorts to ints
877                          * before pushing them. XXX optimise this -tg
878                          */
879                         if (flags & FL_LONG)
880                                 lnum = va_arg(args, unsigned long);
881                         else if ((sizeof(int) < sizeof(long)) && (c == 'd'))
882                                 lnum = (long)va_arg(args, int);
883                         else
884                                 lnum = va_arg(args, unsigned int);
885                         switch (c) {
886                         case 'd':
887                         case 'i':
888                                 if (0 > (long)lnum) {
889                                         lnum = -(long)lnum;
890                                         tmp = 1;
891                                 } else
892                                         tmp = 0;
893                                 /* FALLTHROUGH */
894                         case 'u':
895                                 do {
896                                         *--cp = lnum % 10 + '0';
897                                         lnum /= 10;
898                                 } while (lnum);
899
900                                 if (c != 'u') {
901                                         if (tmp)
902                                                 *--cp = '-';
903                                         else if (flags & FL_PLUS)
904                                                 *--cp = '+';
905                                         else if (flags & FL_BLANK)
906                                                 *--cp = ' ';
907                                 }
908                                 break;
909
910                         case 'o':
911                                 do {
912                                         *--cp = (lnum & 0x7) + '0';
913                                         lnum >>= 3;
914                                 } while (lnum);
915
916                                 if ((flags & FL_HASH) && *cp != '0')
917                                         *--cp = '0';
918                                 break;
919
920                         case 'p':
921                         case 'x': {
922                                 const char *digits = (flags & FL_UPPER) ?
923                                     digits_uc : digits_lc;
924                                 do {
925                                         *--cp = digits[lnum & 0xf];
926                                         lnum >>= 4;
927                                 } while (lnum);
928
929                                 if (flags & FL_HASH) {
930                                         *--cp = (flags & FL_UPPER) ? 'X' : 'x';
931                                         *--cp = '0';
932                                 }
933                         }
934                         }
935                         len = numbuf + sizeof(numbuf) - (s = cp);
936                         if (flags & FL_DOT) {
937                                 if (precision > len) {
938                                         field = precision;
939                                         flags |= FL_ZERO;
940                                 } else
941                                         precision = len; /* no loss */
942                         }
943                         break;
944
945                 case 's':
946                         if (!(s = va_arg(args, const char *)))
947                                 s = "(null)";
948                         len = utf_mbswidth(s);
949                         break;
950
951                 case 'c':
952                         flags &= ~FL_DOT;
953                         numbuf[0] = (char)(va_arg(args, int));
954                         s = numbuf;
955                         len = 1;
956                         break;
957
958                 case '%':
959                 default:
960                         numbuf[0] = c;
961                         s = numbuf;
962                         len = 1;
963                         break;
964                 }
965
966                 /*
967                  * At this point s should point to a string that is to be
968                  * formatted, and len should be the length of the string.
969                  */
970                 if (!(flags & FL_DOT) || len < precision)
971                         precision = len;
972                 if (field > precision) {
973                         field -= precision;
974                         if (!(flags & FL_RIGHT)) {
975                                 field = -field;
976                                 /* skip past sign or 0x when padding with 0 */
977                                 if ((flags & FL_ZERO) && (flags & FL_NUMBER)) {
978                                         if (*s == '+' || *s == '-' ||
979                                             *s == ' ') {
980                                                 shf_putc(*s, shf);
981                                                 s++;
982                                                 precision--;
983                                                 nwritten++;
984                                         } else if (*s == '0') {
985                                                 shf_putc(*s, shf);
986                                                 s++;
987                                                 nwritten++;
988                                                 if (--precision > 0 &&
989                                                     (*s | 0x20) == 'x') {
990                                                         shf_putc(*s, shf);
991                                                         s++;
992                                                         precision--;
993                                                         nwritten++;
994                                                 }
995                                         }
996                                         c = '0';
997                                 } else
998                                         c = flags & FL_ZERO ? '0' : ' ';
999                                 if (field < 0) {
1000                                         nwritten += -field;
1001                                         for ( ; field < 0 ; field++)
1002                                                 shf_putc(c, shf);
1003                                 }
1004                         } else
1005                                 c = ' ';
1006                 } else
1007                         field = 0;
1008
1009                 if (precision > 0) {
1010                         const char *q;
1011
1012                         nwritten += precision;
1013                         q = utf_skipcols(s, precision);
1014                         do {
1015                                 shf_putc(*s, shf);
1016                         } while (++s < q);
1017                 }
1018                 if (field > 0) {
1019                         nwritten += field;
1020                         for ( ; field > 0 ; --field)
1021                                 shf_putc(c, shf);
1022                 }
1023         }
1024
1025         return (shf_error(shf) ? EOF : nwritten);
1026 }
1027
1028 #ifdef MKSH_SMALL
1029 int
1030 shf_getc(struct shf *shf)
1031 {
1032         return ((shf)->rnleft > 0 ? (shf)->rnleft--, *(shf)->rp++ :
1033             shf_getchar(shf));
1034 }
1035
1036 int
1037 shf_putc(int c, struct shf *shf)
1038 {
1039         return ((shf)->wnleft == 0 ? shf_putchar((c), (shf)) :
1040             ((shf)->wnleft--, *(shf)->wp++ = (c)));
1041 }
1042 #endif