2

od (octal dump) command is implemented in Unix since Version 1. However, I couldn't find a reverse command in the Version 6 Unix manual (1975). Modern ways of reversing od with either xxd or sed do not apply to Unix V6, since sed and awk appeared only in Version 7 (while uudecode, xxd and base64 are not available neither).

The questions are:

  • How did people in 1975 convert octal or hex listings into binary files?
  • How can I do this in a PDP-11 emulator that only allows me to paste text into terminal from clipboard? (In other words, is it necessary to write my own program for that?)

Here is a sample hex dump of a binary file by od that I'd like to convert back into binary:

# od -h /bin/sum
0000000 0107 00ba 0000 0204 0000 0000 0000 0001
0000020 1583 0bd6 0ac3 0601 8901 0a37 00ae 15b7
0000040 0002 8905 0000 0000 8609 15c0 0001 8904
0000060 0026 0005 01ef 706f 6472 000a 1001 0a05
0000100 1040 8903 00be 0200 870b 0bc0 030e 0ab7
0000120 007a 15c2 00be 9484 6105 0b45 7e04 01f0
0000140 15c0 0001 8904 0080 0002 09f7 0026 15c0
0000160 0001 8904 0083 0001 1dc5 0050 09f7 0014
0000200 15c0 0001 8904 0082 0001 1040 8906 01c2
0000220 203f 200a 1026 1066 1141 09f7 0006 1581
0000240 1580 0087 0a00 7217 000a 1066 1001 0302
0000260 09f7 fff0 15b7 0012 65f7 0030 000c 15c0
0000300 0001 8904 00ba 0001 0087
0000312 
  • 1
    You should write your own small C program, using scanf(). – meuh Jul 3 at 14:53
  • @meuh, scanf() seems to be unavailable in Unix V6, even though I can see some sources at /usr/source/iolib/scan1.c. Moreover, the C compiler is pre-K&R C, it has =+ and unfamiliar file I/O. Moreover, writing your own tools is agains Unix philosophy... – Andriy Makukha Jul 4 at 11:26
  • 1
    I mentioned scanf() as it was in the manual you linked to. You will have difficulty using existing V6 tools to handle the data as it includes the nul char. For example, man tr has under BUGS: Won't handle ascii NUL in string1 or string2; always deletes NUL from input.. – meuh Jul 4 at 12:15
3

Looks like Version 6 Unix didn't include many common tools yet that appeared only in Version 7 (like sed and awk). At that point, Unix was also not commercialized yet, so "reverse hex dump" could be missing simply because there was no wide demand for that operation or because Ken (or some other programmer) provided such tool from their unofficial /usr directory at Bell Labs. Who knows.

But here is my implementation for reversing a hex dump on Unix V6. When compiled like cc -s -O unhex.c, the resulting executable is only 1160 bytes and works faster than dumping (as expected).

Notice how C language of that time still had compound assignment syntax of B (=+, =*, etc.) and how it expected programmers to provide their own buffers for file I/O.

/* reverse "od -h" operation on Unix V6 */
/* written in pre-K&R C */
/* derived from wc.c and cvopt.c */

int ibuf[259];
int obuf[259];

main(argc,argv)
char **argv;
{
    int token, bytecnt;
    register char *p1, *p2;         /* input buffer pointers */
    register int c;                 /* char or read count */
    char sp, b1, b2, lastc, lastb2, nfirst;

    obuf[0] = 1;                    /* standard output by default */
    if (argc>2) {
                                    /* create output file */
            if ((obuf[0] = creat(argv[2], 0666)) < 0) {
                    diag(argv[2]);
                    diag(": failed to create\n");
                    return;
            }
    }
    if (argc>1 && fopen(argv[1], ibuf)>=0) {
            p1 = 0;
            p2 = 0;
            sp = 0;
            token = 0;
            bytecnt = 0;
            nfirst = 0;
            for(;;) {
                    /* reading from file */
                    if (p1 >= p2) {
                            p1 = &ibuf[1];
                            c = read(ibuf[0], p1, 512);
                            if (c <= 0)
                                    break;
                            p2 = p1+c;
                    }
                    /* decoding loop */
                    c = 0;
                    c =| *p1++;
                    if (c==' ' || c=='\n') {
                            b1 = token;
                            b2 = token >> 8;
                            if (lastc!=' ' && lastc!='\n') {
                                    /* end of token */
                                    if (sp>0) {
                                            if (nfirst) putc(lastb2, obuf);
                                            putc(b1, obuf);
                                            lastb2 = b2;
                                            nfirst = 1;
                                    } else {
                                            /* first token in the line */
                                            bytecnt = token;
                                    }
                            }
                            if (c==' ') sp++;
                            else {
                                    /* new line */
                                    sp = 0;
                                    fflush(obuf);
                            }
                            token = 0;
                    } else {
                            /* actual hex and octal conversion */
                            token =* sp>0 ? 16 : 8;
                            token =+ c<='9' ? c-'0' : c-'W';
                    }
                    lastc = c;
            }
            if (!(bytecnt & 1)) {
                    putc(lastb2, obuf);
                    fflush(obuf);
            }
            close(ibuf[0]);
            close(obuf[0]);
    } else if (argc>1) {
            diag(argv[1]);
            diag(": cannot open\n");
    } else {
            diag("error: filename missing\n");
    }
}

diag(s)
char *s;
{
    while(*s)
            write(2,s++,1);
}

UPD. I published a faster and simpler version on GitHub, where the syntax is also highlighted.

2

Here's a minimal C program you might try to compile in V6 Unix.

#define EOF (-1)
int main(){
  int i,a,b;
  while(scanf("%*7o") != EOF){
    for(i = 0; i<8; i++)
      if(scanf("%2x%2x",&a,&b) != EOF){putchar(a); putchar(b);}
  }
}

If the result ends up byte swapped, you may need to invert the 2 putchars.

  • The result is Undefined:\n_scanf. This is not the first discrepancy between image and manual... Maybe I have some older image, or maybe Unix manuals were just ahead of the time, covering topics that were about to be implemented. Who knows... But scanf is not used in any of the V6 Unix sources, unlike V7 Unix. – Andriy Makukha Jul 5 at 4:00
  • 2
    @AndriyMakukha There is a scanf in version 6 unix. The problem is that it's not in libc, but in libp (look at /usr/source/iolib/run). You'll have to compile the program with cc prog.c -lp. But I haven't tested meuh's program ;-) – mosvy Jul 5 at 11:24
  • @mosvy, thanks, it compiles with libp (albeit to a 7 KB executable with the "squash" option). However, I couldn't make it work. It either crashes with Illegal instruction -- Core dumped messages or outputs something strange. – Andriy Makukha Jul 5 at 12:12
  • 2
    @AndriyMakukha there was no automatic flushing of library buffers (putchar(), printf(), etc) upon exit in that time -- you had to call cexit(exit_status) explicitly (or cclose(1) before returning from main). But meuh's program is swapping the bytes (the pdp11 was little endian!) and has other problems like not handling files whose size is not a multiple of 16 correctly (as the /bin/sum from the Q). – mosvy Jul 6 at 10:43
  • 1
    The sum has also code to open and close files (and handle the errors), and to format the output (~80% of its 202 bytes). A working unhex/unod could simply read from stdin and write to stdout. – mosvy Jul 6 at 11:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.