sbase

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

printf.c (4127B)


      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, f, argi, lastargi, formatlen, blen, nflags;
     23 	long long num;
     24 	double dou;
     25 	int cooldown = 0, width, precision, ret = 0;
     26 	char *format, *tmp, *arg, *fmt;
     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 
     48 		if (format[i] != '%') {
     49 			putchar(format[i]);
     50 			continue;
     51 		}
     52 
     53 		/* flag */
     54 		f = ++i;
     55 		nflags = strspn(&format[f], "#-+ 0");
     56 		i += nflags;
     57 
     58 		if (nflags > INT_MAX)
     59 			eprintf("Too many flags in format\n");
     60 
     61 		/* field width */
     62 		width = -1;
     63 		if (format[i] == '*') {
     64 			if (argi < argc)
     65 				width = estrtonum(argv[argi++], 0, INT_MAX);
     66 			else
     67 				cooldown = 1;
     68 			i++;
     69 		} else {
     70 			j = i;
     71 			i += strspn(&format[i], "+-0123456789");
     72 			if (j != i) {
     73 				tmp = estrndup(format + j, i - j);
     74 				width = estrtonum(tmp, 0, INT_MAX);
     75 				free(tmp);
     76 			} else {
     77 				width = 0;
     78 			}
     79 		}
     80 
     81 		/* field precision */
     82 		precision = -1;
     83 		if (format[i] == '.') {
     84 			if (format[++i] == '*') {
     85 				if (argi < argc)
     86 					precision = estrtonum(argv[argi++], 0, INT_MAX);
     87 				else
     88 					cooldown = 1;
     89 				i++;
     90 			} else {
     91 				j = i;
     92 				i += strspn(&format[i], "+-0123456789");
     93 				if (j != i) {
     94 					tmp = estrndup(format + j, i - j);
     95 					precision = estrtonum(tmp, 0, INT_MAX);
     96 					free(tmp);
     97 				} else {
     98 					precision = 0;
     99 				}
    100 			}
    101 		}
    102 
    103 		if (format[i] != '%') {
    104 			if (argi < argc)
    105 				arg = argv[argi++];
    106 			else {
    107 				arg = "";
    108 				cooldown = 1;
    109 			}
    110 		} else {
    111 			putchar('%');
    112 			continue;
    113 		}
    114 
    115 		switch (format[i]) {
    116 		case 'b':
    117 			if ((tmp = strstr(arg, "\\c"))) {
    118 				*tmp = 0;
    119 				blen = unescape(arg);
    120 				fwrite(arg, sizeof(*arg), blen, stdout);
    121 				return 0;
    122 			}
    123 			blen = unescape(arg);
    124 			fwrite(arg, sizeof(*arg), blen, stdout);
    125 			break;
    126 		case 'c':
    127 			unescape(arg);
    128 			rarg = ereallocarray(NULL, utflen(arg) + 1, sizeof(*rarg));
    129 			utftorunestr(arg, rarg);
    130 			efputrune(rarg, stdout, "<stdout>");
    131 			free(rarg);
    132 			break;
    133 		case 's':
    134 			fmt = emalloc(sizeof("%*.*s" + nflags));
    135 			sprintf(fmt, "%%%.*s*.*s", (int)nflags, &format[f]);
    136 			printf(fmt, width, precision, arg);
    137 			free(fmt);
    138 			break;
    139 		case 'd': case 'i': case 'o': case 'u': case 'x': case 'X':
    140 			for (j = 0; isspace(arg[j]); j++);
    141 			if (arg[j] == '\'' || arg[j] == '\"') {
    142 				arg += j + 1;
    143 				unescape(arg);
    144 				rarg = ereallocarray(NULL, utflen(arg) + 1, sizeof(*rarg));
    145 				utftorunestr(arg, rarg);
    146 				num = rarg[0];
    147 			} else if (arg[0]) {
    148 				errno = 0;
    149 				if (format[i] == 'd' || format[i] == 'i')
    150 					num = strtol(arg, &tmp, 0);
    151 				else
    152 					num = strtoul(arg, &tmp, 0);
    153 
    154 				if (tmp == arg || *tmp != '\0') {
    155 					ret = 1;
    156 					weprintf("%%%c %s: conversion error\n",
    157 					    format[i], arg);
    158 				}
    159 				if (errno == ERANGE) {
    160 					ret = 1;
    161 					weprintf("%%%c %s: out of range\n",
    162 					    format[i], arg);
    163 				}
    164 			} else {
    165 					num = 0;
    166 			}
    167 			fmt = emalloc(sizeof("%*.*ll#") + nflags);
    168 			sprintf(fmt, "%%%.*s*.*ll%c", (int)nflags, &format[f], format[i]);
    169 			printf(fmt, width, precision, num);
    170 			free(fmt);
    171 			break;
    172 		case 'a': case 'A': case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
    173 			fmt = emalloc(sizeof("%*.*#") + nflags);
    174 			sprintf(fmt, "%%%.*s*.*%c", (int)nflags, &format[f], format[i]);
    175 			dou = (strlen(arg) > 0) ? estrtod(arg) : 0;
    176 			printf(fmt, width, precision, dou);
    177 			free(fmt);
    178 			break;
    179 		case '\0':
    180 			eprintf("Missing format specifier.\n");
    181 		default:
    182 			eprintf("Invalid format specifier '%c'.\n", format[i]);
    183 		}
    184 		if (argi >= argc)
    185 			cooldown = 1;
    186 	}
    187 
    188 	return fshut(stdout, "<stdout>") | ret;
    189 }