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