od.c - sbase - suckless unix tools
 (HTM) git clone git://git.suckless.org/sbase
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       od.c (6363B)
       ---
            1 /* See LICENSE file for copyright and license details. */
            2 #include <ctype.h>
            3 #include <fcntl.h>
            4 #include <stdint.h>
            5 #include <stdio.h>
            6 #include <stdlib.h>
            7 #include <string.h>
            8 #include <unistd.h>
            9 
           10 #include "queue.h"
           11 #include "util.h"
           12 
           13 struct type {
           14         unsigned char     format;
           15         unsigned int      len;
           16         TAILQ_ENTRY(type) entry;
           17 };
           18 
           19 static TAILQ_HEAD(head, type) head = TAILQ_HEAD_INITIALIZER(head);
           20 static unsigned char addr_format = 'o';
           21 static off_t skip = 0;
           22 static off_t max = -1;
           23 static size_t linelen = 1;
           24 static int big_endian;
           25 
           26 static void
           27 printaddress(off_t addr)
           28 {
           29         char fmt[] = "%07j#";
           30 
           31         if (addr_format == 'n') {
           32                 fputc(' ', stdout);
           33         } else {
           34                 fmt[4] = addr_format;
           35                 printf(fmt, (intmax_t)addr);
           36         }
           37 }
           38 
           39 static void
           40 printchunk(const unsigned char *s, unsigned char format, size_t len)
           41 {
           42         long long res, basefac;
           43         size_t i;
           44         char fmt[] = " %#*ll#";
           45         unsigned char c;
           46 
           47         const char *namedict[] = {
           48                 "nul", "soh", "stx", "etx", "eot", "enq", "ack",
           49                 "bel", "bs",  "ht",  "nl",  "vt",  "ff",  "cr",
           50                 "so",  "si",  "dle", "dc1", "dc2", "dc3", "dc4",
           51                 "nak", "syn", "etb", "can", "em",  "sub", "esc",
           52                 "fs",  "gs",  "rs",  "us",  "sp",
           53         };
           54         const char *escdict[] = {
           55                 ['\0'] = "\\0", ['\a'] = "\\a",
           56                 ['\b'] = "\\b", ['\t'] = "\\t",
           57                 ['\n'] = "\\n", ['\v'] = "\\v",
           58                 ['\f'] = "\\f", ['\r'] = "\\r",
           59         };
           60 
           61         switch (format) {
           62         case 'a':
           63                 c = *s & ~128; /* clear high bit as required by standard */
           64                 if (c < LEN(namedict) || c == 127) {
           65                         printf(" %3s", (c == 127) ? "del" : namedict[c]);
           66                 } else {
           67                         printf(" %3c", c);
           68                 }
           69                 break;
           70         case 'c':
           71                 if (strchr("\a\b\t\n\v\f\r\0", *s)) {
           72                         printf(" %3s", escdict[*s]);
           73                 } else if (!isprint(*s)) {
           74                         printf(" %3o", *s);
           75                 } else {
           76                         printf(" %3c", *s);
           77                 }
           78                 break;
           79         default:
           80                 if (big_endian) {
           81                         for (res = 0, basefac = 1, i = len; i; i--) {
           82                                 res += s[i - 1] * basefac;
           83                                 basefac <<= 8;
           84                         }
           85                 } else {
           86                         for (res = 0, basefac = 1, i = 0; i < len; i++) {
           87                                 res += s[i] * basefac;
           88                                 basefac <<= 8;
           89                         }
           90                 }
           91                 fmt[2] = big_endian ? '-' : ' ';
           92                 fmt[6] = format;
           93                 printf(fmt, (int)(3 * len + len - 1), res);
           94         }
           95 }
           96 
           97 static void
           98 printline(const unsigned char *line, size_t len, off_t addr)
           99 {
          100         struct type *t = NULL;
          101         size_t i;
          102         int first = 1;
          103         unsigned char *tmp;
          104 
          105         if (TAILQ_EMPTY(&head))
          106                 goto once;
          107         TAILQ_FOREACH(t, &head, entry) {
          108 once:
          109                 if (first) {
          110                         printaddress(addr);
          111                         first = 0;
          112                 } else {
          113                         printf("%*c", (addr_format == 'n') ? 1 : 7, ' ');
          114                 }
          115                 for (i = 0; i < len; i += MIN(len - i, t ? t->len : 4)) {
          116                         if (len - i < (t ? t->len : 4)) {
          117                                 tmp = ecalloc(t ? t->len : 4, 1);
          118                                 memcpy(tmp, line + i, len - i);
          119                                 printchunk(tmp, t ? t->format : 'o',
          120                                            t ? t->len : 4);
          121                                 free(tmp);
          122                         } else {
          123                                 printchunk(line + i, t ? t->format : 'o',
          124                                            t ? t->len : 4);
          125                         }
          126                 }
          127                 fputc('\n', stdout);
          128                 if (TAILQ_EMPTY(&head) || (!len && !first))
          129                         break;
          130         }
          131 }
          132 
          133 static int
          134 od(int fd, char *fname, int last)
          135 {
          136         static unsigned char *line;
          137         static size_t lineoff;
          138         static off_t addr;
          139         unsigned char buf[BUFSIZ];
          140         size_t i, size = sizeof(buf);
          141         ssize_t n;
          142 
          143         while (skip - addr > 0) {
          144                 n = read(fd, buf, MIN(skip - addr, sizeof(buf)));
          145                 if (n < 0)
          146                         weprintf("read %s:", fname);
          147                 if (n <= 0)
          148                         return n;
          149                 addr += n;
          150         }
          151         if (!line)
          152                 line = emalloc(linelen);
          153 
          154         for (;;) {
          155                 if (max >= 0)
          156                         size = MIN(max - (addr - skip), size);
          157                 if ((n = read(fd, buf, size)) <= 0)
          158                         break;
          159                 for (i = 0; i < n; i++, addr++) {
          160                         line[lineoff++] = buf[i];
          161                         if (lineoff == linelen) {
          162                                 printline(line, lineoff, addr - lineoff + 1);
          163                                 lineoff = 0;
          164                         }
          165                 }
          166         }
          167         if (n < 0) {
          168                 weprintf("read %s:", fname);
          169                 return n;
          170         }
          171         if (lineoff && last)
          172                 printline(line, lineoff, addr - lineoff);
          173         if (last)
          174                 printline((unsigned char *)"", 0, addr);
          175         return 0;
          176 }
          177 
          178 static int
          179 lcm(unsigned int a, unsigned int b)
          180 {
          181         unsigned int c, d, e;
          182 
          183         for (c = a, d = b; c ;) {
          184                 e = c;
          185                 c = d % c;
          186                 d = e;
          187         }
          188 
          189         return a / d * b;
          190 }
          191 
          192 static void
          193 addtype(char format, int len)
          194 {
          195         struct type *t;
          196 
          197         t = emalloc(sizeof(*t));
          198         t->format = format;
          199         t->len = len;
          200         TAILQ_INSERT_TAIL(&head, t, entry);
          201 }
          202 
          203 static void
          204 usage(void)
          205 {
          206         eprintf("usage: %s [-bdosvx] [-A addressformat] [-E | -e] [-j skip] "
          207                 "[-t outputformat] [file ...]\n", argv0);
          208 }
          209 
          210 int
          211 main(int argc, char *argv[])
          212 {
          213         int fd;
          214         struct type *t;
          215         int ret = 0, len;
          216         char *s;
          217 
          218         big_endian = (*(uint16_t *)"\0\xff" == 0xff);
          219 
          220         ARGBEGIN {
          221         case 'A':
          222                 s = EARGF(usage());
          223                 if (strlen(s) != 1 || !strchr("doxn", s[0]))
          224                         usage();
          225                 addr_format = s[0];
          226                 break;
          227         case 'b':
          228                 addtype('o', 1);
          229                 break;
          230         case 'd':
          231                 addtype('u', 2);
          232                 break;
          233         case 'E':
          234         case 'e':
          235                 big_endian = (ARGC() == 'E');
          236                 break;
          237         case 'j':
          238                 if ((skip = parseoffset(EARGF(usage()))) < 0)
          239                         usage();
          240                 break;
          241         case 'N':
          242                 if ((max = parseoffset(EARGF(usage()))) < 0)
          243                         usage();
          244                 break;
          245         case 'o':
          246                 addtype('o', 2);
          247                 break;
          248         case 's':
          249                 addtype('d', 2);
          250                 break;
          251         case 't':
          252                 s = EARGF(usage());
          253                 for (; *s; s++) {
          254                         switch (*s) {
          255                         case 'a':
          256                         case 'c':
          257                                 addtype(*s, 1);
          258                                 break;
          259                         case 'd':
          260                         case 'o':
          261                         case 'u':
          262                         case 'x':
          263                                 /* todo: allow multiple digits */
          264                                 if (*(s+1) > '0' && *(s+1) <= '9') {
          265                                         len = *(s+1) - '0';
          266                                 } else {
          267                                         switch (*(s+1)) {
          268                                         case 'C':
          269                                                 len = sizeof(char);
          270                                                 break;
          271                                         case 'S':
          272                                                 len = sizeof(short);
          273                                                 break;
          274                                         case 'I':
          275                                                 len = sizeof(int);
          276                                                 break;
          277                                         case 'L':
          278                                                 len = sizeof(long);
          279                                                 break;
          280                                         default:
          281                                                 len = sizeof(int);
          282                                         }
          283                                 }
          284                                 addtype(*s, len);
          285                                 break;
          286                         default:
          287                                 usage();
          288                         }
          289                 }
          290                 break;
          291         case 'v':
          292                 /* always set - use uniq(1) to handle duplicate lines */
          293                 break;
          294         case 'x':
          295                 addtype('x', 2);
          296                 break;
          297         default:
          298                 usage();
          299         } ARGEND
          300 
          301         /* line length is lcm of type lengths and >= 16 by doubling */
          302         TAILQ_FOREACH(t, &head, entry)
          303                 linelen = lcm(linelen, t->len);
          304         if (TAILQ_EMPTY(&head))
          305                 linelen = 16;
          306         while (linelen < 16)
          307                 linelen *= 2;
          308 
          309         if (!argc) {
          310                 if (od(0, "<stdin>", 1) < 0)
          311                         ret = 1;
          312         } else {
          313                 for (; *argv; argc--, argv++) {
          314                         if (!strcmp(*argv, "-")) {
          315                                 *argv = "<stdin>";
          316                                 fd = 0;
          317                         } else if ((fd = open(*argv, O_RDONLY)) < 0) {
          318                                 weprintf("open %s:", *argv);
          319                                 ret = 1;
          320                                 continue;
          321                         }
          322                         if (od(fd, *argv, (!*(argv + 1))) < 0)
          323                                 ret = 1;
          324                         if (fd != 0)
          325                                 close(fd);
          326                 }
          327         }
          328 
          329         ret |= fshut(stdout, "<stdout>") | fshut(stderr, "<stderr>");
          330 
          331         return ret;
          332 }