xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34226)
1 From stevesu@copper.UUCP Wed Mar 25 23:35:32 1987
2 Path: seismo!ut-sally!husc6!bacchus!mit-eddie!genrad!decvax!tektronix!teklds!copper!stevesu
3 From: stevesu@copper.TEK.COM (Steve Summit)
4 Newsgroups: net.sources
5 Subject: Public Domain _doprnt in C
6 Message-ID: <938@copper.TEK.COM>
7 Date: 26 Mar 87 04:35:32 GMT
8 Reply-To: stevesu@copper.UUCP (Steve Summit)
9 Distribution: world
10 Organization: Tektronix, Inc., Beaverton, OR.
11 Lines: 420
12 
13 Mark Pulver is looking for a C version of _doprnt, so I thought
14 I'd pass mine along.  I wrote this from the ground up; it is
15 absolutely underived from anything proprietary.
16 
17 This version is not complete, and has the following two key
18 omissions:
19 
20 	It doesn't do floating point (%f, %e, or %g).
21 
22 	It will handle %ld (%lx, etc.) incorrectly on machines
23 	where sizeof(long) != sizeof(int).
24 
25 It also does not implement the %# stuff which appeared in the 4.2
26 documentation but which I haven't seen in any implementation yet.
27 
28 I believe it handles everything else correctly, although I have
29 not tested it exhaustively.
30 
31 There are two "fun" additions: %b is binary, and %r is roman.
32 
33 You are free to use this code as you wish, but please leave the
34 identification comment intact.  I can offer no support for this
35 code, although if I ever implement floating point or pdp11
36 support (I'm acutely embarrassed to admit making the typical VAX
37 int/long equivalence assumption) I'll try to remember to post
38 those additions.
39 
40                                            Steve Summit
41                                            stevesu@copper.tek.com
42 
43 --------------------- cut here for doprnt.c ---------------------
44 /*
45  *  Common code for printf et al.
46  *
47  *  The calling routine typically takes a variable number of arguments,
48  *  and passes the address of the first one.  This implementation
49  *  assumes a straightforward, stack implementation, aligned to the
50  *  machine's wordsize.  Increasing addresses are assumed to point to
51  *  successive arguments (left-to-right), as is the case for a machine
52  *  with a downward-growing stack with arguments pushed right-to-left.
53  *
54  *  To write, for example, fprintf() using this routine, the code
55  *
56  *	fprintf(fd, format, args)
57  *	FILE *fd;
58  *	char *format;
59  *	{
60  *	_doprnt(format, &args, fd);
61  *	}
62  *
63  *  would suffice.  (This example does not handle the fprintf's "return
64  *  value" correctly, but who looks at the return value of fprintf
65  *  anyway?)
66  *
67  *  This version implements the following printf features:
68  *
69  *	%d	decimal conversion
70  *	%u	unsigned conversion
71  *	%x	hexadecimal conversion
72  *	%X	hexadecimal conversion with capital letters
73  *	%o	octal conversion
74  *	%c	character
75  *	%s	string
76  *	%m.n	field width, precision
77  *	%-m.n	left adjustment
78  *	%0m.n	zero-padding
79  *	%*.*	width and precision taken from arguments
80  *
81  *  This version does not implement %f, %e, or %g.  It accepts, but
82  *  ignores, an `l' as in %ld, %lo, %lx, and %lu, and therefore will not
83  *  work correctly on machines for which sizeof(long) != sizeof(int).
84  *  It does not even parse %D, %O, or %U; you should be using %ld, %o and
85  *  %lu if you mean long conversion.
86  *
87  *  This version implements the following nonstandard features:
88  *
89  *	%b	binary conversion
90  *	%r	roman numeral conversion
91  *	%R	roman numeral conversion with capital letters
92  *
93  *  As mentioned, this version does not return any reasonable value.
94  *
95  *  Permission is granted to use, modify, or propagate this code as
96  *  long as this notice is incorporated.
97  *
98  *  Steve Summit 3/25/87
99  */
100 
101 #include <stdio.h>
102 
103 #define TRUE 1
104 #define FALSE 0
105 
106 #define ROMAN
107 
108 #define isdigit(d) ((d) >= '0' && (d) <= '9')
109 #define Ctod(c) ((c) - '0')
110 
111 #define MAXBUF (sizeof(long int) * 8)		 /* enough for binary */
112 
113 #ifdef ROMAN
114 static tack();
115 static doit();
116 #endif
117 
118 _doprnt(fmt, argp, fd)
119 register char *fmt;
120 register int *argp;
121 FILE *fd;
122 {
123 register char *p;
124 char *p2;
125 int size;
126 int length;
127 int prec;
128 int ladjust;
129 char padc;
130 int n;
131 unsigned int u;
132 int base;
133 char buf[MAXBUF];
134 int negflag;
135 char *digs;
136 #ifdef ROMAN
137 char *rdigs;
138 int d;
139 #endif
140 
141 while(*fmt != '\0')
142 	{
143 	if(*fmt != '%')
144 		{
145 		putc(*fmt++, fd);
146 		continue;
147 		}
148 
149 	fmt++;
150 
151 	if(*fmt == 'l')
152 		fmt++;	     /* need to use it if sizeof(int) < sizeof(long) */
153 
154 	length = 0;
155 	prec = -1;
156 	ladjust = FALSE;
157 	padc = ' ';
158 
159 	if(*fmt == '-')
160 		{
161 		ladjust = TRUE;
162 		fmt++;
163 		}
164 
165 	if(*fmt == '0')
166 		{
167 		padc = '0';
168 		fmt++;
169 		}
170 
171 	if(isdigit(*fmt))
172 		{
173 		while(isdigit(*fmt))
174 			length = 10 * length + Ctod(*fmt++);
175 		}
176 	else if(*fmt == '*')
177 		{
178 		length = *argp++;
179 		fmt++;
180 		if(length < 0)
181 			{
182 			ladjust = !ladjust;
183 			length = -length;
184 			}
185 		}
186 
187 	if(*fmt == '.')
188 		{
189 		fmt++;
190 		if(isdigit(*fmt))
191 			{
192 			prec = 0;
193 			while(isdigit(*fmt))
194 				prec = 10 * prec + Ctod(*fmt++);
195 			}
196 		else if(*fmt == '*')
197 			{
198 			prec = *argp++;
199 			fmt++;
200 			}
201 		}
202 
203 	negflag = FALSE;
204 	digs = "0123456789abcdef";
205 #ifdef ROMAN
206 	rdigs = "  mdclxvi";
207 #endif
208 
209 	switch(*fmt)
210 		{
211 		case 'b':
212 		case 'B':
213 			u = *argp++;
214 			base = 2;
215 			goto donum;
216 
217 		case 'c':
218 			putc(*argp++, fd);
219 			break;
220 
221 		case 'd':
222 		case 'D':
223 			n = *argp++;
224 
225 			if(n >= 0)
226 				u = n;
227 			else	{
228 				u = -n;
229 				negflag = TRUE;
230 				}
231 
232 			base = 10;
233 
234 			goto donum;
235 
236 		case 'o':
237 		case 'O':
238 			u = *argp++;
239 			base = 8;
240 			goto donum;
241 #ifdef ROMAN
242 		case 'R':
243 			rdigs = "  MDCLXVI";
244 		case 'r':
245 			n = *argp++;
246 			p2 = &buf[MAXBUF - 1];
247 
248 			d = n % 10;
249 			tack(d, &rdigs[6], &p2);
250 			n = n / 10;
251 
252 			d = n % 10;
253 			tack(d, &rdigs[4], &p2);
254 			n = n / 10;
255 
256 			d = n % 10;
257 			tack(d, &rdigs[2], &p2);
258 			n /= 10;
259 
260 			d = n % 10;
261 			tack(d, rdigs, &p2);
262 
263 			p = p2;
264 
265 			goto putpad;
266 #endif
267 		case 's':
268 			p = (char *)(*argp++);
269 
270 			if(p == NULL)
271 				p = "(NULL)";
272 
273 			if(length > 0 && !ladjust)
274 				{
275 				n = 0;
276 				p2 = p;
277 
278 				for(; *p != '\0' &&
279 						(prec == -1 || n < prec); p++)
280 					n++;
281 
282 				p = p2;
283 
284 				while(n < length)
285 					{
286 					putc(' ', fd);
287 					n++;
288 					}
289 				}
290 
291 			n = 0;
292 
293 			while(*p != '\0')
294 				{
295 				if(++n > prec && prec != -1)
296 					break;
297 
298 				putc(*p++, fd);
299 				}
300 
301 			if(n < length && ladjust)
302 				{
303 				while(n < length)
304 					{
305 					putc(' ', fd);
306 					n++;
307 					}
308 				}
309 
310 			break;
311 
312 		case 'u':
313 		case 'U':
314 			u = *argp++;
315 			base = 10;
316 			goto donum;
317 
318 		case 'X':
319 			digs = "0123456789ABCDEF";
320 		case 'x':
321 			u = *argp++;
322 			base = 16;
323 
324 donum:			p = &buf[MAXBUF - 1];
325 
326 			do	{
327 				*p-- = digs[u % base];
328 				u /= base;
329 				} while(u != 0);
330 
331 			if(negflag)
332 				putc('-', fd);
333 putpad:
334 			size = &buf[MAXBUF - 1] - p;
335 
336 			if(size < length && !ladjust)
337 				{
338 				while(length > size)
339 					{
340 					putc(padc, fd);
341 					length--;
342 					}
343 				}
344 
345 			while(++p != &buf[MAXBUF])
346 				putc(*p, fd);
347 
348 			if(size < length)	/* must be ladjust */
349 				{
350 				while(length > size)
351 					{
352 					putc(padc, fd);
353 					length--;
354 					}
355 				}
356 
357 			break;
358 
359 		case '\0':
360 			fmt--;
361 			break;
362 
363 		default:
364 			putc(*fmt, fd);
365 		}
366 	fmt++;
367 	}
368 }
369 
370 #ifdef ROMAN
371 
372 static
373 tack(d, digs, p)
374 int d;
375 char *digs;
376 char **p;
377 {
378 if(d == 0) return;
379 if(d >= 1 && d <= 3)
380 	{
381 	doit(d, digs[2], p);
382 	return;
383 	}
384 
385 if(d == 4 || d == 5)
386 	{
387 	**p = digs[1];
388 	(*p)--;
389 	}
390 
391 if(d == 4)
392 	{
393 	**p = digs[2];
394 	(*p)--;
395 	return;
396 	}
397 
398 if(d == 5) return;
399 
400 if(d >= 6 && d <= 8)
401 	{
402 	doit(d - 5, digs[2], p);
403 	**p = digs[1];
404 	(*p)--;
405 	return;
406 	}
407 
408 /* d == 9 */
409 
410 **p = digs[0];
411 (*p)--;
412 **p = digs[2];
413 (*p)--;
414 return;
415 }
416 
417 static
418 doit(d, one, p)
419 int d;
420 char one;
421 char **p;
422 {
423 int i;
424 
425 for(i = 0; i < d; i++)
426 	{
427 	**p = one;
428 	(*p)--;
429 	}
430 }
431 
432 #endif
433