cleaning
[hexdump.git] / hexdump.c
index 6674970df831478cf7c0317e0cb28de540448195..02814449497c964194fcbd51abc6f53e08e42c1d 100644 (file)
--- a/hexdump.c
+++ b/hexdump.c
@@ -3,7 +3,7 @@
 /* linker: debug.o */
 
 #include <assert.h>
-#include <getopt.h>
+#include <limits.h>
 #include <malloc.h>
 #include <stdio.h>
 #include <stdlib.h>
 //#define BUFFERSIZE 4096
 #define BUFFERSIZE 256
 #define NBCOLS 8
+#define NBDIGITS 6
+#define SEQLEN 32
 
 /* gobal variables */
 
-char *buffer[BUFFERSIZE] = {0};
+int nbcols = NBCOLS;
+int nbdigits = NBDIGITS;
+int offset = 0;
+
+char buffer[BUFFERSIZE] = {0};
 FILE *fin = NULL;
+long int addrfile = 0;
 FILE *fout = NULL;
 char *progname = NULL;
 
+/* type definitions */
+
+typedef struct {
+    char *sequence;
+    char bytes[SEQLEN];
+    int length;
+} sequence_t;
+
 /* help function */
 
-void usage (int ret)
+int usage (int ret)
 {
     FILE *fd = ret ? stderr : stdout;
     fprintf (fd, "usage: %s [-i file] [-h] [-n nbcols] [-o file] [-v]\n", progname);
@@ -41,21 +56,22 @@ void usage (int ret)
     fprintf (fd, " -o: output file\n");
     fprintf (fd, " -v: verbose level (%d)\n", verbose);
     fprintf (fd, "\n");
-    fprintf (fd, "commands: [/hstr/|0xaddr] [a hstr] [d nb|-] [i hstr] [p nb|-] [s/h1/h2/[g]]\n");
-    fprintf (fd, " 0x: move to address addr\n");
-    fprintf (fd, " //: move to hexa stringi hstr\n");
+    fprintf (fd, "commands: [/hstr/|addr|+nb] [a hstr] [d nb|-] [i hstr] [p nb|-] [s/h1/h2/[g]]\n");
+    fprintf (fd, " addr: move to address (0... octal, [1-9]... deci, 0x... hexa)\n");
+    fprintf (fd, " +nb: move to offset (0... octal, [1-9]... deci, 0x... hexa)\n");
+    fprintf (fd, " //: move to hexa string hstr\n");
     fprintf (fd, " a : append hexa string hstr to current address\n");
     fprintf (fd, " d : delete nb bytes (- until end file)\n");
     fprintf (fd, " i : insert hexa string hstr to current address\n");
     fprintf (fd, " p : print nb bytes (- until end file)\n");
     fprintf (fd, " s : substitute h1 by h2 (g for globally)\n");
 
-    exit (ret);
+    return ret;
 }
 
 /* get number of digits */
 
-int getnbdigits (long int l) {
+unsigned int getnbdigits (unsigned long int l) {
     int n = 0;
     while (l) {
         n += 2;
@@ -66,12 +82,12 @@ int getnbdigits (long int l) {
 
 /* print a line */
 
-void printline (char *buffer, int nbcols, int nb, int addr, int nbdigits) {
+void printline (char *buffer, int nb, unsigned long int addr) {
     int i;
 
-    printf ("0x%0*x:", nbdigits, addr);
+    printf ("0x%0*lx:", nbdigits, addr);
     for (i = 0; i < nb; i++) {
-        printf (" %02x", buffer[i]);
+        printf (" %02x", (unsigned char)buffer[i]);
     }
     for (i = nb; i < nbcols; i++) {
         printf ("   ");
@@ -93,26 +109,117 @@ int writefile (char *pt, int nb) {
     return 1;
 }
 
+/* search sequence function */
+
+int searchseq (sequence_t *seq) {
+    char *pt = buffer;
+    int nb = 0;
+    int i, j;
+    int valid = 0;
+
+    VERBOSE (DEBUG, printf ("search sequence: %s\n", seq->sequence));
+
+    while (!feof (fin)) {
+        int nbread = fread (pt, 1, BUFFERSIZE - (pt - buffer), fin);
+        nb += nbread;
+        pt = buffer;
+        for (i = 0; i < nb - seq->length; i++) {
+            valid = 1;
+            for (j = 0; (j < seq->length) && (valid); j++) {
+                if (pt[i + j] != seq->bytes[j]) {
+                    valid = 0;
+                }
+            }
+            if (valid) {
+                break;
+            }
+        }
+
+        if (!valid) {
+            writefile (buffer, nb - seq->length);
+            offset = 0;
+            addrfile += nb - seq->length;
+            for (i = 0; i < seq->length; i++) {
+                buffer[i] = buffer[nb - seq->length + i];
+            }
+            pt = buffer + seq->length;
+            nb = seq->length;
+        } else {
+            writefile (buffer, i);
+            offset = seq->length;
+            addrfile += i;
+            fseek (fin, i - nb, SEEK_CUR);
+            VERBOSE (DEBUG, printf ("found sequence at 0x%0*lx\n", getnbdigits (addrfile), addrfile));
+            return 0;
+        }
+    }
+
+    if (!valid) {
+        writefile (buffer, nb);
+        addrfile += seq->length;
+    }
+
+    return 1;
+}
+
+/* go to address function */
+
+int gotoaddr (long int addr) {
+    char buffer[BUFFERSIZE] = {0};
+
+    if (addr == -1) {
+        addr = LONG_MAX;
+    } else if (addrfile > addr) {
+        return 1;
+    }
+
+    VERBOSE (DEBUG, printf ("look for address: 0x%04lx\n", addr));
+    while (!feof (fin)) {
+        int nbtoread = (addrfile + BUFFERSIZE > addr) ? addr - addrfile : BUFFERSIZE;
+        int nbread = fread (buffer, 1, nbtoread, fin);
+        writefile (buffer, nbread);
+        addrfile += nbread;
+        if (addrfile == addr) {
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+/* insert sequence function */
+
+int insertseq (sequence_t *seq) {
+    char buffer[BUFFERSIZE] = {0};
+
+    VERBOSE (DEBUG, printf ("insert (%d): '%s'\n", offset, seq->sequence);
+                    int i;
+                    for (i = 0; i < seq->length; i++) {
+                        char c = seq->bytes[i];
+                        printf (" 0x%02x (%c)", c, ((c >= 32) && (c < 127)) ? c : '.');
+                    };
+                    printf ("\n"));
+    if (offset > 0) {
+        int nbread = fread (buffer, 1, offset, fin);
+        if (nbread != offset) {
+            return 1;
+        }
+        writefile (buffer, offset);
+        offset = 0;
+    }
+    writefile (seq->bytes, seq->length);
+
+    return 0;
+}
+
 /* hexadecimal dump function */
 
-int hexdump (int nbcols, int len) {
+int hexdump (int len) {
     char buffer[BUFFERSIZE] = {0};
     int i;
 
     char *pt = buffer;
 
-    /* get file size */
-    int nbdigits = 0;
-    if (fin != stdin) {
-        fseek (fin, 0 , SEEK_END);
-        long int filesize = ftell (fin);
-        fseek (fin, 0 , SEEK_SET);
-        nbdigits = getnbdigits (filesize);
-    } else {
-        nbdigits = 6;
-    }
-
-    int addr = 0;
     int nb = 0;
     while (!feof (fin)) {
         int nbtoread = BUFFERSIZE - (pt - buffer);
@@ -128,10 +235,10 @@ int hexdump (int nbcols, int len) {
 
         /* print line */
         while ((nb - (int)(pt - buffer)) / nbcols > 0) {
-            printline (pt, nbcols, nbcols, addr, nbdigits);
+            printline (pt, nbcols, addrfile);
             writefile (pt, nbcols);
+            addrfile += nbcols;
             pt += nbcols;
-            addr += nbcols;
         }
 
         /* copy end buffer */
@@ -149,8 +256,198 @@ int hexdump (int nbcols, int len) {
 
     /* last line */
     if (nb > 0) {
-        printline (buffer, nbcols, nb, addr, nbdigits);
-        writefile (pt, nb);
+        printline (buffer, nb, addrfile);
+        writefile (buffer, nb);
+        addrfile += nb;
+    }
+
+    return 0;
+}
+
+/* parse octal string */
+
+long int octal (char *s, int n) {
+    int i;
+    unsigned long int l = 0;
+    for (i = 0; i < n; i++) {
+        if ((s[i] >= '0') && (s[i] < '8')) {
+            l = l * 8 + s[i] - '0';
+        } else {
+            return -1;
+        }
+    }
+    return l;
+}
+
+/* parse hexa string */
+
+long int hexa (char *s, int n) {
+    int i;
+    unsigned long int l = 0;
+    for (i = 0; i < n; i++) {
+        l *= 16;
+        if ((s[i] >= '0') && (s[i] <= '9')) {
+            l += s[i] - '0';
+        } else if ((s[i] >= 'A') && (s[i] <= 'F')) {
+            l += s[i] + 10 - 'A';
+        } else if ((s[i] >= 'a') && (s[i] <= 'f')) {
+            l += s[i] + 10 - 'a';
+        } else {
+            return -1;
+        }
+    }
+    return l;
+}
+
+/* special character function */
+
+int specialchar (char *s, char *b) {
+    int i = 0, j = 0;
+    while (s[i] != 0) {
+        if (j == SEQLEN) {
+            return 0;
+        }
+        if (s[i] != '\\') {
+            b[j++] = s[i++];
+            continue;
+        }
+
+        int l = -1;
+        switch (s[i + 1]) {
+        case 'a': l = 0x07; i += 2; break;
+        case 'b': l = 0x08; i += 2; break;
+        case 'e': l = 0x1b; i += 2; break;
+        case 'f': l = 0x0c; i += 2; break;
+        case 'n': l = 0x0a; i += 2; break;
+        case 'r': l = 0x0d; i += 2; break;
+        case 't': l = 0x09; i += 2; break;
+        case 'v': l = 0x0b; i += 2; break;
+        case '/': l = '/'; i += 2; break;
+        case '\\': l = '\\'; i += 2; break;
+        case '\'': l = '\''; i += 2; break;
+        case '"': l = '"'; i += 2; break;
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+            l = octal (s + i + 1, 3);
+            if (l == -1) {
+                VERBOSE (WARNING, fprintf (stderr, "incorrect special char (\\%c%c%c)\n", s[i + 1], s[i + 2], s[i + 3]));
+            }
+            i += 4;
+            break;
+        case 'x':
+            l = hexa (s + i + 2, 2);
+            if (l == -1) {
+                VERBOSE (WARNING, fprintf (stderr, "incorrect special char (\\x%c%c)\n", s[i + 2], s[i + 3]));
+            }
+            i += 4;
+            break;
+        default:
+            VERBOSE (WARNING, fprintf (stderr, "incorrect special char (\\%c)\n", s[i + 1]));
+            i += 2;
+            break;
+        }
+        if (l != -1) {
+            VERBOSE (DEBUG, printf("l: 0x%02x '%c'\n", l, l));
+            b[j++] = l;
+        }
+    }
+
+    return j;
+}
+
+/* remove space function */
+
+void removespace (char *s, char **p) {
+    while (*s) {
+        if ((*s == ' ') || (*s == '\t')) {
+            s++;
+        } else {
+            break;
+        }
+    }
+    if (p != NULL) {
+        *p = s;
+    }
+}
+
+/* get pattern function */
+
+int getpattern (sequence_t *seq, char *s, char **p) {
+
+    seq->sequence = s;
+    seq->length = 0;
+
+    while (*s) {
+        if ((*s == '\\') && ((s[1] == '/') || (s[1] == '\\'))) {
+            s++;
+        } else if (*s == '/') {
+            *s++ = 0;
+            break;
+        }
+        s++;
+    }
+    seq->length = specialchar (seq->sequence, seq->bytes);
+
+    if (p != NULL) {
+        *p = s;
+    }
+
+    return (seq->length == 0);
+}
+
+/* get hexa sequence function */
+
+int gethexaseq (sequence_t *seq, char *s, char **p) {
+    int i = 0;
+
+    seq->sequence = s;
+    seq->length = 0;
+
+    while (*s) {
+        if (((*s >= '0') && (*s <= '9')) ||
+            ((*s >= 'A') && (*s <= 'F')) ||
+            ((*s >= 'a') && (*s <= 'f'))) {
+            s++;
+            i++;
+            if (i % 2 == 0) {
+                seq->bytes[seq->length] = hexa (seq->sequence + 2 * seq->length, 2);
+                if (seq->bytes[seq->length] == -1) {
+                    return 1;
+                }
+                seq->length++;
+            }
+        } else {
+            break;
+        }
+    }
+
+    if (p != NULL) {
+        *p = s;
+    }
+
+    return (seq->length == 0) || (i % 2 == 1);
+}
+
+/* get length function */
+
+long int getlength (char *s, char **p) {
+
+    while (*s != '\0') {
+        if ((*s == ' ') || (*s == '\t')) {
+            s++;
+        } else if ((*s >= '0') && (*s <= '9')) {
+            return strtol (s, p, 10);
+        } else if (*s == '-') {
+            if (p != NULL) {
+                *p = s + 1;
+            }
+            return -1;
+        } else {
+            VERBOSE (ERROR, fprintf (stderr, "unknown length (%s)\n", s));
+            return 0;
+        }
     }
 
     return 0;
@@ -158,15 +455,17 @@ int hexdump (int nbcols, int len) {
 
 /* main function */
 
-int main (int argc, char *argv[]) 
+int main (int argc, char *argv[])
 {
     int rc = 0;
     char *input = NULL;
     char *output = NULL;
-    int nbcols = NBCOLS;
     char *commands = NULL;
-    int printlen = -1;
-   
+    long int length = -1;
+    sequence_t seq = {0};
+    unsigned long int addr = 0;
+    char c;
+
     /* get basename */
     char *pt = progname = argv[0];
     while (*pt) {
@@ -176,48 +475,64 @@ int main (int argc, char *argv[])
         pt++;
     }
 
-    int c;
-    while ((c = getopt(argc, argv, "e:i:hn:o:v:")) != EOF) {
+     while (argc-- > 1) {
+        char *arg = *(++argv);
+        if (arg[0] != '-') {
+            VERBOSE (ERROR, fprintf (stderr, "%s: invalid option -- %s\n", progname, arg));
+            return usage (1);
+        }
+        char c = arg[1];
         switch (c) {
-        case 'i':
-            input = optarg;
-            break;
         case 'e':
-            if (commands == NULL) {
-                commands = optarg;
-            } else {
-                strcat (commands, " ");
-                strcat (commands, optarg);
+            arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
+            if (arg) {
+                //commands = (commands == NULL) ? arg :
+                //    strcat (strcat (commands, " "), arg);
+                if (commands == NULL) {
+                    commands = arg;
+                } else {
+                    char *tmp = (char *) malloc (strlen (arg) + 1);
+                    strcat (strcat (commands, " "), strcpy (tmp, arg));
+                    free (tmp);
+                }
             }
             break;
+        case 'i':
+            input = (arg[2]) ? arg + 2 : (--argc > 0 ) ? *(++argv) : NULL;
+            break;
         case 'n':
-            nbcols = atoi (optarg);
+            arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
+            if (arg == NULL) {
+                VERBOSE (ERROR, fprintf (stderr, "%s: missing number of columns\n", progname));
+                return usage (1);
+            }
+            nbcols = atoi (arg);
             break;
         case 'o':
-            output = optarg;
+            output = (arg[2]) ? arg + 2 : (--argc > 0 ) ? *(++argv) : NULL;
             break;
         case 'v':
-            verbose = atoi (optarg);
+            arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
+            if (arg == NULL) {
+                VERBOSE (ERROR, fprintf (stderr, "%s: missing verbose level\n", progname));
+                return usage (1);
+            }
+            verbose = atoi (arg);
             break;
         case 'h':
         default:
-            usage (c != 'h');
+            return usage (c != 'h');
         }
     }
-    if (argc - optind != 0) {
-        fprintf (stderr, "%s: invalid option -- %s\n", progname, argv[optind]);
-        usage (1);
-    }
 
     /* check input */
+    fin = stdin;
     if (input) {
         fin = fopen (input, "rb");
         if (!fin) {
             VERBOSE (ERROR, fprintf (stderr, "error: can't open file '%s'\n", input));
             return 1;
         }
-    } else {
-        fin = stdin;
     }
 
     /* check output */
@@ -232,57 +547,162 @@ int main (int argc, char *argv[])
         //fout = stdout;
     }
 
+    /* get file size */
+    if (fin != stdin) {
+        fseek (fin, 0 , SEEK_END);
+        unsigned long int filesize = ftell (fin);
+        fseek (fin, 0 , SEEK_SET);
+        nbdigits = getnbdigits (filesize);
+    }
+
     if (commands == NULL) {
-        hexdump (nbcols, -1);
+        VERBOSE (DEBUG, printf ("no command\n"));
+        hexdump (-1);
     } else {
         VERBOSE (DEBUG, printf ("commands: %s\n", commands));
         while ((*commands != '\0') && (rc == 0)) {
-            switch (*commands++) {
+            switch (c = *commands++) {
             case ' ':
             case '\t':
                 break;
 
-            case '/': /* read patern */
+            case '/': /* search pattern */
+                rc = getpattern (&seq, commands, &commands);
+                if (rc == 0) {
+                    rc = searchseq (&seq);
+                    if (rc == 1) {
+                        VERBOSE (ERROR, fprintf (stderr, "can't find pattern '%s'\n", seq.sequence));
+                    }
+                } else {
+                    VERBOSE (ERROR, fprintf (stderr, "erroneous pattern \"%s'\n", seq.sequence));
+                }
                 break;
 
             case '0': /* read address */
+                if (*commands == 'x') {
+                    commands++;
+                    addr = strtol (commands, &commands, 16);
+                } else {
+                    addr = strtol (commands, &commands, 8);
+                }
+                if (addr) {
+                    rc = gotoaddr (addr);
+                    if (rc == 1) {
+                        VERBOSE (ERROR, fprintf (stderr, "can't find address (0x%0*lx)\n", getnbdigits (addr), addr));
+                    }
+                } else {
+                    VERBOSE (ERROR, fprintf (stderr, "erroneous address\n"));
+                    rc = 1;
+                }
+                offset = 0;
                 break;
 
-            case 'a': /* append mode */
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9': /* read address */
+                commands--;
+                addr = strtol (commands, &commands, 10);
+                if ((*commands != 0) && (*commands != ' ')) {
+                    VERBOSE (ERROR, fprintf (stderr, "erroneous address ()\n"));
+                    rc = 1;
+                } else {
+                    rc = gotoaddr (addr);
+                    if (rc == 1) {
+                        VERBOSE (ERROR, fprintf (stderr, "can't find address (0x%0*lx)\n", getnbdigits (addr), addr));
+                    }
+                    offset = 0;
+                }
                 break;
 
-            case 'd': /* delete mode */
-                break;
+            case 'a': /* append mode */
+                offset = 0;
+                /* fall through */
 
             case 'i': /* insert mode */
+                removespace (commands, &commands);
+                rc = gethexaseq (&seq, commands, &commands);
+                if (rc == 0) {
+                    rc = insertseq (&seq);
+                    if (rc == 1) {
+                        VERBOSE (ERROR, fprintf (stderr, "can't jump (%d)\n", offset));
+                    }
+                } else {
+                    VERBOSE (ERROR, fprintf (stderr, "erroneous sequence '%s'\n", seq.sequence));
+                }
+                offset = 0;
                 break;
 
+            case '+': /* relative move */
+                /* fall through */
+
+            case 'd': /* delete mode */
+                /* fall through */
+
             case 'p': /* print mode */
-                printlen = -1;
-                while (*commands != '\0') {
-                    if ((*commands == ' ') || (*commands == '\t')) {
-                        commands++;
-                    } else if ((*commands >= '0') && (*commands <= '9')) {
-                        printlen = strtol (commands, &commands, 10);
+                length = getlength (commands, &commands);
+                if (length == 0){
+                    VERBOSE (ERROR, fprintf (stderr, "erroneous length\n"));
+                    rc = 1;
+                } else {
+                    switch (c) {
+                    case '+':
+                        rc = gotoaddr ((length > 0) ? addrfile + length : -1);
+                        if (rc == 1) {
+                            VERBOSE (ERROR, fprintf (stderr, "can't find address (0x%0*lx)\n", getnbdigits (addr), addr));
+                        }
                         break;
-                    } else if (*commands == '-') {
-                        printlen = -1;
-                        commands++;
+
+                    case 'd':
+                        fseek (fin, length, SEEK_CUR);
                         break;
-                    } else {
-                        VERBOSE (ERROR, fprintf (stderr, "unkown print lenght (%s)\n", commands));
-                        rc = 1;
+
+                    case 'p':
+                        hexdump (length);
                         break;
                     }
                 }
-                if (rc == 0) hexdump (nbcols, printlen);
+                offset = 0;
                 break;
 
             case 's': /* substitute mode */
+                if (*commands == '/') {
+                    rc = getpattern (&seq, ++commands, &commands);
+                    if (rc == 0) {
+                        rc = searchseq (&seq);
+                        if (rc == 0) {
+                            fseek (fin, offset, SEEK_CUR);
+                            rc = gethexaseq (&seq, commands, &commands);
+                            commands++;
+                            if (rc == 0) {
+                                offset = 0;
+                                rc = insertseq (&seq);
+                                if (rc == 1) {
+                                    VERBOSE (ERROR, fprintf (stderr, "can't jump (%d)\n", offset));
+                                }
+                            } else {
+                                VERBOSE (ERROR, fprintf (stderr, "erroneous sequence '%s'\n", seq.sequence));
+                            }
+                        } else {
+                            VERBOSE (ERROR, fprintf (stderr, "can't find pattern '%s'\n", seq.sequence));
+                        }
+                    } else {
+                        VERBOSE (ERROR, fprintf (stderr, "erroneous pattern '%s'\n", seq.sequence));
+                    }
+                } else {
+                    VERBOSE (ERROR, fprintf (stderr, "erroneous sequence '%s'\n", seq.sequence));
+                    rc = 1;
+                }
+                offset = 0;
                 break;
 
             default:
-                VERBOSE (ERROR, fprintf (stderr, "unknown command (%c)\n", commands[-1]));
+                VERBOSE (ERROR, fprintf (stderr, "unknown command '%c'\n", commands[-1]));
                 rc = 1;
             }
         }
@@ -305,14 +725,54 @@ int main (int argc, char *argv[])
     return rc;
 }
 
-// test: hexdump.exe -h
 // test: hexdump.exe -h | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
+// test: hexdump.exe foo 2>&1 | grep -q 'invalid option'
+// test: hexdump.exe -n 2>&1 | grep -q 'missing number of columns'
+// test: hexdump.exe -v 2>&1 | grep -q 'missing verbose level'
 // test: hexdump.exe -_ 2> /dev/null | awk 'END { if (NR == 0) { exit(0) } else exit (1) }'
 // test: hexdump.exe -_ 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
 // test: hexdump.exe -i hexdump.c | grep -q '0x[0-9a-f]*: '
+// test: hexdump.exe -i hexdump.ko 2>&1 | grep -q "can't open file"
+// test: hexdump.exe -i hexdump.c -o ko/test.c 2>&1 | grep -q "can't open file"
+// test: cat hexdump.c | hexdump.exe -n 3 | head -2 | tail -1 | grep -q '0x000003: 64 65 70  dep'
 // test: hexdump.exe -i hexdump.c -n 3 | head -2 | tail -1 | grep -q '0x0003: 64 65 70  dep'
+// test: hexdump.exe -i hexdump.c -v 3 -n 8 | grep -q 'no command'
 // test: hexdump.exe -i hexdump.c -o test.c -e 'p 200' | tail -1 | grep -q '0x00c0:'
-// test: cmp hexdump.c test.c
-// test: rm test.c
+// test: cmp hexdump.c test.c; x=$?; rm test.c; test x$x = x0
+// test: hexdump.exe -i hexdump.c -n 8 -e ' /cflags/ p 17 /debug/ p 8' | grep -q '0x0019: 2a 2f 0a 2f 2a 20 6c 69  \*\/\./\* li'
+// test: hexdump.exe -i hexdump.c -e ' /\099\411\xgg\y/' 2>&1 | grep -c 'incorrect special char' | xargs test 4 -eq
+// test: hexdump.exe -i hexdump.c -o test.c -e ' /cfl\x61gs/ p 16 /d\145bug/ p 8' | grep -q '0x0027: 64 65 62 75 67 2e 6f 20  debug.o'
+// test: cmp hexdump.c test.c; x=$?; rm test.c; test x$x = x0
+// test: hexdump.exe -i hexdump.c -e ' /\n/ p 8' | grep -q '0x000d: 0a 2f 2a 20 63 66 6c 61  \./\* cfla'
+// test: hexdump.exe -i hexdump.c -o test.c -e ' /\a\b\e\f\r\t\v/ p 8'; x=$?; test x$x = x1
+// test: cmp hexdump.c test.c; x=$?; rm test.c; test x$x = x0
+// test: hexdump.exe -i hexdump.c -v 3 -e " /\'/" -e ' /\"/' -e ' /\\/' -e ' /\x2a/' -e ' /\x3A/' | grep l: | wc -l | xargs test 5 =
+// test: hexdump.exe -i hexdump.c -e ' /\n\/* vim:/ p -' | grep -q ': 74 3a 20 2a 2f 0a *t: \*\/\.'
+// test: hexdump.exe -i hexdump.c -e 'p go_to_end' 2>&1 | grep -q 'unknown length'
+// test: hexdump.exe -i hexdump.c -e ' /\x41BCD/' 2>&1 | grep -q "can't find pattern"
+// test: hexdump.exe -i hexdump.c -e ' //' 2>&1 | grep -q 'erroneous pattern'
+// test: hexdump.exe -i hexdump.c -e 'foo' 2>&1 | grep -q 'unknown command'
+// test: hexdump.exe -i hexdump.c -e '0x20 p 8 64 p 8 0200 p 16' | grep -q '0x0080:'
+// test: hexdump.exe -i hexdump.c -e '07777777' 2>&1 | grep -q "can't find address"
+// test: hexdump.exe -i hexdump.c -e '0xFFFFFF' 2>&1 | grep -q "can't find address"
+// test: hexdump.exe -i hexdump.c -e '99999999' 2>&1 | grep -q "can't find address"
+// test: hexdump.exe -i hexdump.c -e '+9999999' 2>&1 | grep -q "can't find address"
+// test: hexdump.exe -i hexdump.c -e '09' 2>&1 | grep -q 'erroneous address'
+// test: hexdump.exe -i hexdump.c -e '0xg' 2>&1 | grep -q 'erroneous address'
+// test: hexdump.exe -i hexdump.c -e '1a' 2>&1 | grep -q 'erroneous address'
+// test: hexdump.exe -i hexdump.c -o test.c -e ' /cflags/ a 414e5a /link/ i 2F333B'
+// test: grep -q '[A]NZcflags' test.c && grep -q '[l]ink/3;er' test.c; x=$?; rm test.c; test x$x = x0
+// test: hexdump.exe -i hexdump.c -e ' /cflags/ a 414e5' 2>&1 | grep -q 'erroneous sequence'
+// test: hexdump.exe -i hexdump.c -o test.c -e ' /lags/ d 2'
+// test: grep -q '[c]fgs' test.c; x=$?; rm test.c; test x$x = x0
+// test: hexdump.exe -i hexdump.c -o test.c -e ' /lags/ +2 i 2041'
+// test: grep -q '[c]fla Ags:' test.c; x=$?; rm test.c; test x$x = x0
+// test: hexdump.exe -i hexdump.c -o test.c -e ' s/lags/2041/'
+// test: grep -q '[c]f A:' test.c; x=$?; rm test.c; test x$x = x0
+// test: hexdump.exe -i hexdump.c -e ' s' 2>&1 | grep -q 'erroneous sequence'
+// test: hexdump.exe -i hexdump.c -e ' s//' 2>&1 | grep -q 'erroneous pattern'
+// test: hexdump.exe -i hexdump.c -e ' s/\x41BCD/2041/' 2>&1 | grep -q "can't find pattern"
+// test: hexdump.exe -i hexdump.c -e ' s/cflags/414e5/' 2>&1 | grep -q 'erroneous sequence'
+// test: hexdump.exe -i hexdump.exe | grep -q 'ffff'; test x$? = x1
 
 /* vim: set ts=4 sw=4 et: */