1 /* 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. 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 #if !defined(BUILTIN) && !defined(SHELL) 35 #ifndef lint 36 static char const copyright[] = 37 "@(#) Copyright (c) 1989, 1993\n\ 38 The Regents of the University of California. All rights reserved.\n"; 39 #endif /* not lint */ 40 #endif 41 42 #ifndef lint 43 #if 0 44 static char const sccsid[] = "@(#)printf.c 8.1 (Berkeley) 7/20/93"; 45 #endif 46 static const char rcsid[] = 47 "$FreeBSD$"; 48 #endif /* not lint */ 49 50 #include <sys/types.h> 51 52 #include <err.h> 53 #include <errno.h> 54 #include <limits.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <unistd.h> 59 60 #ifdef SHELL 61 #define main printfcmd 62 #include "bltin/bltin.h" 63 #include "memalloc.h" 64 #else 65 #define warnx1(a, b, c) warnx(a) 66 #define warnx2(a, b, c) warnx(a, b) 67 #define warnx3(a, b, c) warnx(a, b, c) 68 #endif 69 70 #ifndef BUILTIN 71 #include <locale.h> 72 #endif 73 74 #define PF(f, func) { \ 75 char *b = NULL; \ 76 if (fieldwidth) \ 77 if (precision) \ 78 (void)asprintf(&b, f, fieldwidth, precision, func); \ 79 else \ 80 (void)asprintf(&b, f, fieldwidth, func); \ 81 else if (precision) \ 82 (void)asprintf(&b, f, precision, func); \ 83 else \ 84 (void)asprintf(&b, f, func); \ 85 if (b) { \ 86 (void)fputs(b, stdout); \ 87 free(b); \ 88 } \ 89 } 90 91 static int asciicode __P((void)); 92 static void escape __P((char *)); 93 static int getchr __P((void)); 94 static double getdouble __P((void)); 95 static int getint __P((int *)); 96 static int getquad __P((quad_t *)); 97 static char *getstr __P((void)); 98 static char *mklong __P((char *, int)); 99 static void usage __P((void)); 100 101 static char **gargv; 102 103 int 104 #ifdef BUILTIN 105 progprintf(argc, argv) 106 #else 107 main(argc, argv) 108 #endif 109 int argc; 110 char *argv[]; 111 { 112 static char *skip1, *skip2; 113 int ch, end, fieldwidth, precision; 114 char convch, nextch, *format, *fmt, *start; 115 116 #ifndef BUILTIN 117 (void) setlocale(LC_NUMERIC, ""); 118 #endif 119 while ((ch = getopt(argc, argv, "")) != -1) 120 switch (ch) { 121 case '?': 122 default: 123 usage(); 124 return (1); 125 } 126 argc -= optind; 127 argv += optind; 128 129 if (argc < 1) { 130 usage(); 131 return (1); 132 } 133 134 /* 135 * Basic algorithm is to scan the format string for conversion 136 * specifications -- once one is found, find out if the field 137 * width or precision is a '*'; if it is, gather up value. Note, 138 * format strings are reused as necessary to use up the provided 139 * arguments, arguments of zero/null string are provided to use 140 * up the format string. 141 */ 142 skip1 = "#-+ 0"; 143 skip2 = "0123456789"; 144 145 escape(fmt = format = *argv); /* backslash interpretation */ 146 gargv = ++argv; 147 for (;;) { 148 end = 0; 149 /* find next format specification */ 150 next: for (start = fmt;; ++fmt) { 151 if (!*fmt) { 152 /* avoid infinite loop */ 153 if (end == 1) { 154 warnx1("missing format character", 155 NULL, NULL); 156 return (1); 157 } 158 end = 1; 159 if (fmt > start) 160 (void)printf("%s", start); 161 if (!*gargv) 162 return (0); 163 fmt = format; 164 goto next; 165 } 166 /* %% prints a % */ 167 if (*fmt == '%') { 168 if (*++fmt != '%') 169 break; 170 *fmt++ = '\0'; 171 (void)printf("%s", start); 172 goto next; 173 } 174 } 175 176 /* skip to field width */ 177 for (; strchr(skip1, *fmt); ++fmt); 178 if (*fmt == '*') { 179 if (getint(&fieldwidth)) 180 return (1); 181 ++fmt; 182 } else { 183 fieldwidth = 0; 184 185 /* skip to possible '.', get following precision */ 186 for (; strchr(skip2, *fmt); ++fmt); 187 } 188 if (*fmt == '.') { 189 /* precision present? */ 190 ++fmt; 191 if (*fmt == '*') { 192 if (getint(&precision)) 193 return (1); 194 ++fmt; 195 } else { 196 precision = 0; 197 198 /* skip to conversion char */ 199 for (; strchr(skip2, *fmt); ++fmt); 200 } 201 } else 202 precision = 0; 203 if (!*fmt) { 204 warnx1("missing format character", NULL, NULL); 205 return (1); 206 } 207 208 convch = *fmt; 209 nextch = *++fmt; 210 *fmt = '\0'; 211 switch(convch) { 212 case 'c': { 213 char p; 214 215 p = getchr(); 216 PF(start, p); 217 break; 218 } 219 case 's': { 220 char *p; 221 222 p = getstr(); 223 PF(start, p); 224 break; 225 } 226 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { 227 quad_t p; 228 char *f; 229 230 if ((f = mklong(start, convch)) == NULL) 231 return (1); 232 if (getquad(&p)) 233 return (1); 234 PF(f, p); 235 break; 236 } 237 case 'e': case 'E': case 'f': case 'g': case 'G': { 238 double p; 239 240 p = getdouble(); 241 PF(start, p); 242 break; 243 } 244 default: 245 warnx2("illegal format character %c", convch, NULL); 246 return (1); 247 } 248 *fmt = nextch; 249 } 250 /* NOTREACHED */ 251 } 252 253 static char * 254 mklong(str, ch) 255 char *str; 256 int ch; 257 { 258 static char *copy; 259 static size_t copy_size; 260 size_t len, newlen; 261 char *newcopy; 262 263 len = strlen(str) + 2; 264 if (len > copy_size) { 265 newlen = ((len + 1023) >> 10) << 10; 266 #ifdef SHELL 267 if ((newcopy = ckrealloc(copy, newlen)) == NULL) 268 #else 269 if ((newcopy = realloc(copy, newlen)) == NULL) 270 #endif 271 return (NULL); 272 copy = newcopy; 273 copy_size = newlen; 274 } 275 276 memmove(copy, str, len - 3); 277 copy[len - 3] = 'q'; 278 copy[len - 2] = ch; 279 copy[len - 1] = '\0'; 280 return (copy); 281 } 282 283 static void 284 escape(fmt) 285 register char *fmt; 286 { 287 register char *store; 288 register int value, c; 289 290 for (store = fmt; (c = *fmt); ++fmt, ++store) { 291 if (c != '\\') { 292 *store = c; 293 continue; 294 } 295 switch (*++fmt) { 296 case '\0': /* EOS, user error */ 297 *store = '\\'; 298 *++store = '\0'; 299 return; 300 case '\\': /* backslash */ 301 case '\'': /* single quote */ 302 *store = *fmt; 303 break; 304 case 'a': /* bell/alert */ 305 *store = '\7'; 306 break; 307 case 'b': /* backspace */ 308 *store = '\b'; 309 break; 310 case 'f': /* form-feed */ 311 *store = '\f'; 312 break; 313 case 'n': /* newline */ 314 *store = '\n'; 315 break; 316 case 'r': /* carriage-return */ 317 *store = '\r'; 318 break; 319 case 't': /* horizontal tab */ 320 *store = '\t'; 321 break; 322 case 'v': /* vertical tab */ 323 *store = '\13'; 324 break; 325 /* octal constant */ 326 case '0': case '1': case '2': case '3': 327 case '4': case '5': case '6': case '7': 328 for (c = 3, value = 0; 329 c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { 330 value <<= 3; 331 value += *fmt - '0'; 332 } 333 --fmt; 334 *store = value; 335 break; 336 default: 337 *store = *fmt; 338 break; 339 } 340 } 341 *store = '\0'; 342 } 343 344 static int 345 getchr() 346 { 347 if (!*gargv) 348 return ('\0'); 349 return ((int)**gargv++); 350 } 351 352 static char * 353 getstr() 354 { 355 if (!*gargv) 356 return (""); 357 return (*gargv++); 358 } 359 360 static char *Number = "+-.0123456789"; 361 static int 362 getint(ip) 363 int *ip; 364 { 365 quad_t val; 366 367 if (getquad(&val)) 368 return (1); 369 if (val < INT_MIN || val > INT_MAX) { 370 warnx3("%s: %s", *gargv, strerror(ERANGE)); 371 return (1); 372 } 373 *ip = (int)val; 374 return (0); 375 } 376 377 static int 378 getquad(lp) 379 quad_t *lp; 380 { 381 quad_t val; 382 char *ep; 383 384 if (!*gargv) { 385 *lp = 0; 386 return (0); 387 } 388 if (strchr(Number, **gargv)) { 389 errno = 0; 390 val = strtoq(*gargv, &ep, 0); 391 if (*ep != '\0') { 392 warnx2("%s: illegal number", *gargv, NULL); 393 return (1); 394 } 395 if (errno == ERANGE) 396 if (val == QUAD_MAX) { 397 warnx3("%s: %s", *gargv, strerror(ERANGE)); 398 return (1); 399 } 400 if (val == QUAD_MIN) { 401 warnx3("%s: %s", *gargv, strerror(ERANGE)); 402 return (1); 403 } 404 405 *lp = val; 406 ++gargv; 407 return (0); 408 } 409 *lp = (long)asciicode(); 410 return (0); 411 } 412 413 static double 414 getdouble() 415 { 416 if (!*gargv) 417 return ((double)0); 418 if (strchr(Number, **gargv)) 419 return (atof(*gargv++)); 420 return ((double)asciicode()); 421 } 422 423 static int 424 asciicode() 425 { 426 register int ch; 427 428 ch = **gargv; 429 if (ch == '\'' || ch == '"') 430 ch = (*gargv)[1]; 431 ++gargv; 432 return (ch); 433 } 434 435 static void 436 usage() 437 { 438 (void)fprintf(stderr, "usage: printf format [arg ...]\n"); 439 } 440