1) Added s/key support .
[unix-history] / usr.bin / printf / printf.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
49b60b62 35#if !defined(SHELL) && !defined(BUILTIN)
15637ed4
RG
36char copyright[] =
37"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
38 All rights reserved.\n";
49b60b62 39#endif
15637ed4
RG
40#endif /* not lint */
41
42#ifndef lint
49b60b62
C
43/*static char sccsid[] = "from: @(#)printf.c 5.9 (Berkeley) 6/1/90";*/
44static char rcsid[] = "$Id: printf.c,v 1.8 1993/11/19 21:08:17 jtc Exp $";
15637ed4
RG
45#endif /* not lint */
46
49b60b62 47#include <ctype.h>
15637ed4 48#include <stdio.h>
49b60b62
C
49#include <stdlib.h>
50#include <string.h>
51#include <limits.h>
52#include <locale.h>
53#include <err.h>
54
55static int print_escape_str __P((const char *));
56static int print_escape __P((const char *));
57
58static int getchr __P((void));
59static double getdouble __P((void));
60static int getint __P((void));
61static long getlong __P((void));
62static char *getstr __P((void));
63static char *mklong __P((char *, int));
64static void usage __P((void));
65
66static int rval;
67static char **gargv;
68
69#define isodigit(c) ((c) >= '0' && (c) <= '7')
70#define octtobin(c) ((c) - '0')
71#define hextobin(c) ((c) >= 'A' && 'c' <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0')
72
73#ifdef SHELL
74#define main printfcmd
75#include "../../bin/sh/bltin/bltin.h"
76
77#ifdef __STDC__
78#include <stdarg.h>
79#else
80#include <vararg.h>
81#endif
82
83static void
84#ifdef __STDC__
85warnx(const char *fmt, ...)
86#else
87warnx(fmt, va_alist)
88 const char *fmt;
89 va_dcl
90#endif
91{
92
93 char buf[64];
94 va_list ap;
95
96#ifdef __STDC__
97 va_start(ap, fmt);
98#else
99 va_start(ap);
100#endif
101 vsprintf(buf, fmt, ap);
102 va_end(ap);
103
104 error(buf);
105}
106#endif /* SHELL */
15637ed4
RG
107
108#define PF(f, func) { \
109 if (fieldwidth) \
110 if (precision) \
111 (void)printf(f, fieldwidth, precision, func); \
112 else \
113 (void)printf(f, fieldwidth, func); \
114 else if (precision) \
115 (void)printf(f, precision, func); \
116 else \
117 (void)printf(f, func); \
118}
119
49b60b62
C
120int
121#ifdef BUILTIN
122progprintf(argc, argv)
123#else
15637ed4 124main(argc, argv)
49b60b62 125#endif
15637ed4
RG
126 int argc;
127 char **argv;
128{
49b60b62
C
129 register char *fmt, *start;
130 register int fieldwidth, precision;
131 char convch, nextch;
132 char *format;
133 int ch;
134
135#if !defined(SHELL) && !defined(BUILTIN)
136 setlocale (LC_ALL, "");
137#endif
138
139 while ((ch = getopt(argc, argv, "")) != -1) {
140 switch (ch) {
141 case '?':
142 default:
143 usage();
144 return (1);
145 }
146 }
147 argc -= optind;
148 argv += optind;
149
150 if (argc < 1) {
151 usage();
152 return (1);
15637ed4
RG
153 }
154
49b60b62 155 format = *argv;
15637ed4 156 gargv = ++argv;
49b60b62
C
157
158#define SKIP1 "#-+ 0"
159#define SKIP2 "*0123456789"
160 do {
161 /*
162 * Basic algorithm is to scan the format string for conversion
163 * specifications -- once one is found, find out if the field
164 * width or precision is a '*'; if it is, gather up value.
165 * Note, format strings are reused as necessary to use up the
166 * provided arguments, arguments of zero/null string are
167 * provided to use up the format string.
168 */
169
15637ed4 170 /* find next format specification */
49b60b62
C
171 for (fmt = format; *fmt; fmt++) {
172 switch (*fmt) {
173 case '%':
174 start = fmt++;
175
176 if (*fmt == '%') {
177 putchar ('%');
178 break;
179 } else if (*fmt == 'b') {
180 char *p = getstr();
181 if (print_escape_str(p)) {
182 return (rval);
183 }
184 break;
15637ed4 185 }
49b60b62
C
186
187 /* skip to field width */
188 for (; index(SKIP1, *fmt); ++fmt);
189 fieldwidth = *fmt == '*' ? getint() : 0;
190
191 /* skip to possible '.', get following precision */
192 for (; index(SKIP2, *fmt); ++fmt);
193 if (*fmt == '.')
194 ++fmt;
195 precision = *fmt == '*' ? getint() : 0;
196
197 for (; index(SKIP2, *fmt); ++fmt);
198 if (!*fmt) {
199 warnx ("missing format character");
200 rval = 1;
201 return;
202 }
203
204 convch = *fmt;
205 nextch = *(fmt + 1);
206 *(fmt + 1) = '\0';
207 switch(convch) {
208 case 'c': {
209 char p = getchr();
210 PF(start, p);
211 break;
212 }
213 case 's': {
214 char *p = getstr();
215 PF(start, p);
216 break;
217 }
218 case 'd':
219 case 'i':
220 case 'o':
221 case 'u':
222 case 'x':
223 case 'X': {
224 char *f = mklong(start, convch);
225 long p = getlong();
226 PF(f, p);
227 break;
228 }
229 case 'e':
230 case 'E':
231 case 'f':
232 case 'g':
233 case 'G': {
234 double p = getdouble();
235 PF(start, p);
15637ed4 236 break;
49b60b62
C
237 }
238 default:
239 warnx ("%s: invalid directive", start);
240 rval = 1;
241 }
242 *(fmt + 1) = nextch;
243 break;
244
245 case '\\':
246 fmt += print_escape(fmt);
247 break;
248
249 default:
250 putchar (*fmt);
251 break;
15637ed4
RG
252 }
253 }
49b60b62 254 } while (gargv > argv && *gargv);
15637ed4 255
49b60b62
C
256 return (rval);
257}
15637ed4 258
49b60b62
C
259
260/*
261 * Print SysV echo(1) style escape string
262 * Halts processing string and returns 1 if a \c escape is encountered.
263 */
264static int
265print_escape_str(str)
266 register const char *str;
267{
268 int value;
269 int c;
270
271 while (*str) {
272 if (*str == '\\') {
273 str++;
274 /*
275 * %b string octal constants are not like those in C.
276 * They start with a \0, and are followed by 0, 1, 2,
277 * or 3 octal digits.
278 */
279 if (*str == '0') {
280 str++;
281 for (c = 3, value = 0; c-- && isodigit(*str); str++) {
282 value <<= 3;
283 value += octtobin(*str);
284 }
285 putchar (value);
286 str--;
287 } else if (*str == 'c') {
288 return 1;
289 } else {
290 str--;
291 str += print_escape(str);
292 }
293 } else {
294 putchar (*str);
15637ed4 295 }
49b60b62
C
296 str++;
297 }
298
299 return 0;
300}
301
302/*
303 * Print "standard" escape characters
304 */
305static int
306print_escape(str)
307 register const char *str;
308{
309 const char *start = str;
310 int c;
311 int value;
312
313 str++;
314
315 switch (*str) {
316 case '0': case '1': case '2': case '3':
317 case '4': case '5': case '6': case '7':
318 for (c = 3, value = 0; c-- && isodigit(*str); str++) {
319 value <<= 3;
320 value += octtobin(*str);
15637ed4 321 }
49b60b62
C
322 putchar(value);
323 return str - start - 1;
324 /* NOTREACHED */
325
326 case 'x':
327 str++;
328 for (value = 0; isxdigit(*str); str++) {
329 value <<= 4;
330 value += hextobin(*str);
15637ed4 331 }
49b60b62
C
332 if (value > UCHAR_MAX) {
333 warnx ("escape sequence out of range for character");
334 rval = 1;
15637ed4 335 }
49b60b62
C
336 putchar (value);
337 return str - start - 1;
338 /* NOTREACHED */
339
340 case '\\': /* backslash */
341 putchar('\\');
342 break;
343
344 case '\'': /* single quote */
345 putchar('\'');
346 break;
347
348 case '"': /* double quote */
349 putchar('"');
350 break;
351
352 case 'a': /* alert */
353#ifdef __STDC__
354 putchar('\a');
355#else
356 putchar(007);
357#endif
358 break;
359
360 case 'b': /* backspace */
361 putchar('\b');
362 break;
363
364 case 'e': /* escape */
365#ifdef __GNUC__
366 putchar('\e');
367#else
368 putchar(033);
369#endif
370 break;
371
372 case 'f': /* form-feed */
373 putchar('\f');
374 break;
375
376 case 'n': /* newline */
377 putchar('\n');
378 break;
379
380 case 'r': /* carriage-return */
381 putchar('\r');
382 break;
383
384 case 't': /* tab */
385 putchar('\t');
386 break;
387
388 case 'v': /* vertical-tab */
389 putchar('\v');
390 break;
391
392 default:
393 putchar(*str);
394 warnx("unknown escape sequence `\\%c'", *str);
395 rval = 1;
15637ed4 396 }
49b60b62
C
397
398 return 1;
15637ed4
RG
399}
400
49b60b62 401static char *
15637ed4
RG
402mklong(str, ch)
403 char *str, ch;
404{
49b60b62
C
405 static char copy[64];
406 int len;
15637ed4
RG
407
408 len = strlen(str) + 2;
49b60b62 409 memmove(copy, str, len - 3);
15637ed4
RG
410 copy[len - 3] = 'l';
411 copy[len - 2] = ch;
412 copy[len - 1] = '\0';
49b60b62 413 return (copy);
15637ed4
RG
414}
415
49b60b62 416static int
15637ed4
RG
417getchr()
418{
419 if (!*gargv)
420 return((int)'\0');
421 return((int)**gargv++);
422}
423
49b60b62 424static char *
15637ed4
RG
425getstr()
426{
427 if (!*gargv)
428 return("");
429 return(*gargv++);
430}
431
432static char *number = "+-.0123456789";
49b60b62 433static int
15637ed4
RG
434getint()
435{
436 if (!*gargv)
437 return(0);
49b60b62 438
15637ed4
RG
439 if (index(number, **gargv))
440 return(atoi(*gargv++));
49b60b62
C
441
442 return 0;
15637ed4
RG
443}
444
49b60b62 445static long
15637ed4
RG
446getlong()
447{
49b60b62
C
448 long val;
449 char *ep;
15637ed4
RG
450
451 if (!*gargv)
49b60b62
C
452 return(0L);
453
454 if (**gargv == '\"' || **gargv == '\'') {
455 val = gargv[0][1];
456 gargv++;
457 return val;
458 }
459
460 val = strtol (*gargv, &ep, 0);
461 if (*ep) {
462 warnx ("incompletely converted argument: %s", *gargv);
463 rval = 1;
464 }
465
466 gargv++;
467 return val;
15637ed4
RG
468}
469
49b60b62 470static double
15637ed4
RG
471getdouble()
472{
49b60b62
C
473 double val;
474 char *ep;
15637ed4
RG
475
476 if (!*gargv)
49b60b62
C
477 return(0.0);
478
479 if (**gargv == '\"' || **gargv == '\'') {
480 val = gargv[0][1];
481 gargv++;
482 return val;
483 }
484
485 val = strtod (*gargv, &ep);
486 if (*ep) {
487 warnx ("incompletely converted argument: %s", *gargv);
488 rval = 1;
489 }
490
491 gargv++;
492 return val;
15637ed4
RG
493}
494
49b60b62
C
495static void
496usage()
15637ed4 497{
49b60b62 498 (void)fprintf(stderr, "usage: printf format [arg ...]\n");
15637ed4 499}