printf.c - sbase - suckless unix tools
 (HTM) git clone git://git.suckless.org/sbase
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) 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 }