printf.c (7588B)
1 /* 2 * printf - print a text string 3 * 4 * Gunnar Ritter, Freiburg i. Br., Germany, June 2005. 5 */ 6 /* 7 * Copyright (c) 2005 Gunnar Ritter 8 * 9 * This software is provided 'as-is', without any express or implied 10 * warranty. In no event will the authors be held liable for any damages 11 * arising from the use of this software. 12 * 13 * Permission is granted to anyone to use this software for any purpose, 14 * including commercial applications, and to alter it and redistribute 15 * it freely, subject to the following restrictions: 16 * 17 * 1. The origin of this software must not be misrepresented; you must not 18 * claim that you wrote the original software. If you use this software 19 * in a product, an acknowledgment in the product documentation would be 20 * appreciated but is not required. 21 * 22 * 2. Altered source versions must be plainly marked as such, and must not be 23 * misrepresented as being the original software. 24 * 25 * 3. This notice may not be removed or altered from any source distribution. 26 */ 27 28 #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4 29 #define USED __attribute__ ((used)) 30 #elif defined __GNUC__ 31 #define USED __attribute__ ((unused)) 32 #else 33 #define USED 34 #endif 35 static const char sccsid[] USED = "@(#)printf.c 1.7 (gritter) 7/17/05"; 36 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <libgen.h> 40 #include <locale.h> 41 #include <wchar.h> 42 #include <limits.h> 43 #include <errno.h> 44 #include "asciitype.h" 45 46 #if defined (__GLIBC__) && defined (_IO_getc_unlocked) 47 #undef putchar 48 #define putchar(c) _IO_putc_unlocked(c, stdout) 49 #endif 50 51 static char *fp; /* format pointer */ 52 static int a; /* current argument index */ 53 static int ab; /* beginning of arguments */ 54 static int ac; /* argc to main() */ 55 static char **av; /* argv to main() */ 56 static int c; /* last character (byte) read */ 57 static const char *progname; /* argv[0] to main() */ 58 static int status; /* exit status */ 59 static int mb_cur_max; /* MB_CUR_MAX */ 60 static int dolflag; /* n$ field encountered */ 61 62 static void 63 usage(void) 64 { 65 fprintf(stderr, "Usage: %s format [[[arg1] arg2] ... argn]\n", 66 progname); 67 exit(2); 68 } 69 70 #define getnum(T, type, func) static type \ 71 T(const char *cp) \ 72 { \ 73 char *xp; \ 74 wchar_t wc; \ 75 int i; \ 76 type n; \ 77 \ 78 if (*cp == '"' || *cp == '\'') { \ 79 if (mb_cur_max > 1 && cp[1] & 0200) { \ 80 if ((i = mbtowc(&wc, &cp[1], mb_cur_max)) < 0) \ 81 return WEOF; \ 82 return wc; \ 83 } else \ 84 return cp[1] & 0377; \ 85 } \ 86 errno = 0; \ 87 n = func(cp, &xp); \ 88 if (errno) { \ 89 fprintf(stderr, "%s: \"%s\" arithmetic overflow\n", \ 90 progname, cp); \ 91 status |= 1; \ 92 xp = ""; \ 93 } \ 94 if (*xp) { \ 95 fprintf(stderr, "%s: \"%s\" %s\n", progname, cp, \ 96 xp > cp ? "not completely converted" : \ 97 "expected numeric value"); \ 98 status |= 1; \ 99 } \ 100 return n; \ 101 } 102 103 #define getint(a, b) strtol(a, b, 0) 104 #define getuns(a, b) strtoul(a, b, 0) 105 #define getdouble(a, b) strtod(a, b) 106 107 getnum(integer, int, getint) 108 getnum(unsgned, unsigned, getuns) 109 getnum(floating, double, getdouble) 110 111 static int 112 backslash(int bflag, int really) 113 { 114 int c, i, n, z = 1; 115 116 fp++; 117 if (mb_cur_max > 1 && *fp & 0200) { 118 if ((n = mblen(fp, mb_cur_max)) < 0) 119 n = 1; 120 } else 121 n = 1; 122 switch (*fp) { 123 case '\0': 124 n = 0; 125 /*FALLTHRU*/ 126 case '\\': 127 if (really) putchar('\\'); 128 break; 129 case 'a': 130 if (really) putchar('\a'); 131 break; 132 case 'b': 133 if (really) putchar('\b'); 134 break; 135 case 'c': 136 if (bflag) { 137 if (really) 138 exit(status); 139 else { 140 while (*fp) 141 fp++; 142 return 0; 143 } 144 } 145 goto dfl; 146 case 'f': 147 if (really) putchar('\f'); 148 break; 149 case 'n': 150 if (really) putchar('\n'); 151 break; 152 case 'r': 153 if (really) putchar('\r'); 154 break; 155 case 't': 156 if (really) putchar('\t'); 157 break; 158 case 'v': 159 if (really) putchar('\v'); 160 break; 161 case '0': 162 if (bflag) { 163 if (fp[1]) { 164 fp++; 165 goto digit; 166 } 167 if (really) putchar('\0'); 168 break; 169 } 170 /*FALLTHRU*/ 171 case '1': case '2': case '3': 172 case '4': case '5': case '6': case '7': 173 if (bflag) 174 goto dfl; 175 digit: c = 0; 176 for (i = 0; i < 3 && octalchar(fp[i] & 0377); i++) 177 c = c << 3 | (fp[i] - '0'); 178 if (really) putchar(c); 179 n = i; 180 break; 181 default: 182 dfl: for (i = 0; i < n; i++) 183 if (really) putchar(fp[i] & 0377); 184 z = n; 185 } 186 fp += n - 1; 187 return z; 188 } 189 190 static void 191 bconv(int width, int prec, char *sp) 192 { 193 char *ofp = fp; 194 int i, n, really = 1; 195 196 fp = sp; 197 if (width > 0) { 198 really = 0; 199 goto try; 200 prt: really = 1; 201 fp = sp; 202 for (i = 0; i < width - n && i + n < prec; i++) 203 putchar(' '); 204 } 205 try: for (n = 0; *fp && n < prec; fp++) { 206 switch (*fp) { 207 case '\\': 208 n += backslash(1, really); 209 break; 210 default: 211 if (really) putchar(*fp & 0377); 212 n++; 213 } 214 } 215 if (width > 0 && really == 0) 216 goto prt; 217 fp = ofp; 218 if (width < 0) { 219 while (n < prec && n++ < -width) 220 putchar(' '); 221 } 222 } 223 224 #define nextarg() (a < ac ? av[a++] : "") 225 static void 226 percent(void) 227 { 228 char *fmt = fp, *sp; 229 int width = 0, prec = LONG_MAX; 230 int n; 231 double f; 232 int c; 233 int star = 0; 234 int sign = 1; 235 236 if (*++fp == '\0') { 237 fp--; 238 return; 239 } 240 if (digitchar(*fp&0377)) { 241 n = 0; 242 for (sp = fp; digitchar(*sp&0377); sp++) 243 n = n * 10 + *sp - '0'; 244 if (*sp == '$') { 245 a = n + ab - 1; 246 dolflag = 1; 247 fmt = sp; 248 fmt[0] = '%'; 249 fp = &sp[1]; 250 } 251 } 252 loop: switch (*fp) { 253 case '-': 254 sign = -1; 255 /*FALLTHRU*/ 256 case '+': 257 case '#': 258 case '0': 259 case ' ': 260 fp++; 261 goto loop; 262 } 263 if (digitchar(*fp&0377)) { 264 do 265 width = width * 10 + *fp++ - '0'; 266 while (digitchar(*fp&0377)); 267 } else if (*fp == '*') { 268 width = a < ac ? integer(av[a++]) : 0; 269 fp++; 270 star |= 1; 271 } 272 width *= sign; 273 if (*fp == '.') { 274 fp++; 275 if (digitchar(*fp&0377)) { 276 prec = 0; 277 do 278 prec = prec * 10 + *fp++ - '0'; 279 while (digitchar(*fp&0377)); 280 } else if (*fp == '*') { 281 prec = a < ac ? integer(av[a++]) : 0; 282 fp++; 283 star |= 2; 284 } 285 } 286 switch (*fp) { 287 case 'b': 288 bconv(width, prec, nextarg()); 289 return; 290 case '%': 291 putchar('%'); 292 return; 293 case 'd': 294 case 'i': 295 case 'o': 296 case 'u': 297 case 'x': 298 case 'X': 299 case 'c': 300 n = *fp == 'c' ? *(nextarg()) & 0377 : 301 *fp == 'd' || *fp == 'i' ? 302 integer(nextarg()) : 303 unsgned(nextarg()); 304 c = fp[1]; 305 fp[1] = '\0'; 306 switch (star) { 307 case 3: 308 printf(fmt, width, prec, n); 309 break; 310 case 2: 311 printf(fmt, prec, n); 312 break; 313 case 1: 314 printf(fmt, width, n); 315 break; 316 default: 317 printf(fmt, n); 318 } 319 fp[1] = c; 320 break; 321 case 'f': 322 case 'e': 323 case 'E': 324 case 'g': 325 case 'G': 326 f = floating(nextarg()); 327 c = fp[1]; 328 fp[1] = '\0'; 329 switch (star) { 330 case 3: 331 printf(fmt, width, prec, f); 332 break; 333 case 2: 334 printf(fmt, prec, f); 335 break; 336 case 1: 337 printf(fmt, width, f); 338 break; 339 default: 340 printf(fmt, f); 341 } 342 fp[1] = c; 343 break; 344 case 's': 345 c = fp[1]; 346 fp[1] = '\0'; 347 sp = nextarg(); 348 switch (star) { 349 case 3: 350 printf(fmt, width, prec, sp); 351 break; 352 case 2: 353 printf(fmt, prec, sp); 354 break; 355 case 1: 356 printf(fmt, width, sp); 357 break; 358 default: 359 printf(fmt, sp); 360 } 361 fp[1] = c; 362 break; 363 default: 364 putchar(*fp & 0377); 365 return; 366 } 367 } 368 369 int 370 main(int argc, char **argv) 371 { 372 setlocale(LC_CTYPE, ""); 373 mb_cur_max = MB_CUR_MAX; 374 progname = basename(argv[0]); 375 if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '-' && 376 argv[1][2] == '\0') { 377 argv++; 378 argc--; 379 } 380 if (argc <= 1) 381 usage(); 382 ac = argc; 383 av = argv; 384 a = ab = 2; 385 do { 386 for (fp = av[1]; *fp; fp++) { 387 switch (c = *fp & 0377) { 388 case '\\': 389 backslash(0, 1); 390 break; 391 case '%': 392 percent(); 393 break; 394 default: 395 putchar(c); 396 } 397 } 398 } while (a > ab && a < ac && dolflag == 0); 399 if (ferror(stdout)) 400 status |= 1; 401 return status; 402 }