sbase

suckless unix tools
git clone git://git.2f30.org/sbase.git
Log | Files | Refs | README | LICENSE

printf.c (3919B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <ctype.h>
      3 #include <errno.h>
      4 #include <limits.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 
      9 #include "utf.h"
     10 #include "util.h"
     11 
     12 static void
     13 usage(void)
     14 {
     15 	eprintf("usage: %s format [arg ...]\n", argv0);
     16 }
     17 
     18 int
     19 main(int argc, char *argv[])
     20 {
     21 	Rune *rarg;
     22 	size_t i, j, argi, lastargi, formatlen, blen;
     23 	long long num;
     24 	double dou;
     25 	int cooldown = 0, width, precision, ret = 0;
     26 	char *format, *tmp, *arg, *fmt, flag;
     27 
     28 	argv0 = argv[0];
     29 	if (argc < 2)
     30 		usage();
     31 
     32 	format = argv[1];
     33 	if ((tmp = strstr(format, "\\c"))) {
     34 		*tmp = 0;
     35 		cooldown = 1;
     36 	}
     37 	formatlen = unescape(format);
     38 	if (formatlen == 0)
     39 		return 0;
     40 	lastargi = 0;
     41 	for (i = 0, argi = 2; !cooldown || i < formatlen; i++, i = cooldown ? i : (i % formatlen)) {
     42 		if (i == 0) {
     43 			if (lastargi == argi)
     44 				break;
     45 			lastargi = argi;
     46 		}
     47 		if (format[i] != '%') {
     48 			putchar(format[i]);
     49 			continue;
     50 		}
     51 
     52 		/* flag */
     53 		for (flag = '\0', i++; strchr("#-+ 0", format[i]); i++) {
     54 			flag = format[i];
     55 		}
     56 
     57 		/* field width */
     58 		width = -1;
     59 		if (format[i] == '*') {
     60 			if (argi < argc)
     61 				width = estrtonum(argv[argi++], 0, INT_MAX);
     62 			else
     63 				cooldown = 1;
     64 			i++;
     65 		} else {
     66 			j = i;
     67 			for (; strchr("+-0123456789", format[i]); i++);
     68 			if (j != i) {
     69 				tmp = estrndup(format + j, i - j);
     70 				width = estrtonum(tmp, 0, INT_MAX);
     71 				free(tmp);
     72 			} else {
     73 				width = 0;
     74 			}
     75 		}
     76 
     77 		/* field precision */
     78 		precision = -1;
     79 		if (format[i] == '.') {
     80 			if (format[++i] == '*') {
     81 				if (argi < argc)
     82 					precision = estrtonum(argv[argi++], 0, INT_MAX);
     83 				else
     84 					cooldown = 1;
     85 				i++;
     86 			} else {
     87 				j = i;
     88 				for (; strchr("+-0123456789", format[i]); i++);
     89 				if (j != i) {
     90 					tmp = estrndup(format + j, i - j);
     91 					precision = estrtonum(tmp, 0, INT_MAX);
     92 					free(tmp);
     93 				} else {
     94 					precision = 0;
     95 				}
     96 			}
     97 		}
     98 
     99 		if (format[i] != '%') {
    100 			if (argi < argc)
    101 				arg = argv[argi++];
    102 			else {
    103 				arg = "";
    104 				cooldown = 1;
    105 			}
    106 		} else {
    107 			putchar('%');
    108 			continue;
    109 		}
    110 
    111 		switch (format[i]) {
    112 		case 'b':
    113 			if ((tmp = strstr(arg, "\\c"))) {
    114 				*tmp = 0;
    115 				blen = unescape(arg);
    116 				fwrite(arg, sizeof(*arg), blen, stdout);
    117 				return 0;
    118 			}
    119 			blen = unescape(arg);
    120 			fwrite(arg, sizeof(*arg), blen, stdout);
    121 			break;
    122 		case 'c':
    123 			unescape(arg);
    124 			rarg = ereallocarray(NULL, utflen(arg) + 1, sizeof(*rarg));
    125 			utftorunestr(arg, rarg);
    126 			efputrune(rarg, stdout, "<stdout>");
    127 			free(rarg);
    128 			break;
    129 		case 's':
    130 			printf("%*.*s", width, precision, arg);
    131 			break;
    132 		case 'd': case 'i': case 'o': case 'u': case 'x': case 'X':
    133 			for (j = 0; isspace(arg[j]); j++);
    134 			if (arg[j] == '\'' || arg[j] == '\"') {
    135 				arg += j + 1;
    136 				unescape(arg);
    137 				rarg = ereallocarray(NULL, utflen(arg) + 1, sizeof(*rarg));
    138 				utftorunestr(arg, rarg);
    139 				num = rarg[0];
    140 			} else if (arg[0]) {
    141 				errno = 0;
    142 				if (format[i] == 'd' || format[i] == 'i')
    143 					num = strtol(arg, &tmp, 0);
    144 				else
    145 					num = strtoul(arg, &tmp, 0);
    146 
    147 				if (tmp == arg || *tmp != '\0') {
    148 					ret = 1;
    149 					weprintf("%%%c %s: conversion error\n",
    150 					    format[i], arg);
    151 				}
    152 				if (errno == ERANGE) {
    153 					ret = 1;
    154 					weprintf("%%%c %s: out of range\n",
    155 					    format[i], arg);
    156 				}
    157 			} else {
    158 					num = 0;
    159 			}
    160 			fmt = estrdup(flag ? "%#*.*ll#" : "%*.*ll#");
    161 			if (flag)
    162 				fmt[1] = flag;
    163 			fmt[flag ? 7 : 6] = format[i];
    164 			printf(fmt, width, precision, num);
    165 			free(fmt);
    166 			break;
    167 		case 'a': case 'A': case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
    168 			fmt = estrdup(flag ? "%#*.*#" : "%*.*#");
    169 			if (flag)
    170 				fmt[1] = flag;
    171 			fmt[flag ? 5 : 4] = format[i];
    172 			dou = (strlen(arg) > 0) ? estrtod(arg) : 0;
    173 			printf(fmt, width, precision, dou);
    174 			free(fmt);
    175 			break;
    176 		default:
    177 			eprintf("Invalid format specifier '%c'.\n", format[i]);
    178 		}
    179 		if (argi >= argc)
    180 			cooldown = 1;
    181 	}
    182 
    183 	return fshut(stdout, "<stdout>") | ret;
    184 }