Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> PDP-7 assembly listing for “pd”

> Unidentified program.

> Might “pd” stand for “previous directory”?

I know approximately zero PDP-7 assembly, but this looks like it might be “print directory”, equivalent to pwd today? It seems to open its parent “dotdot” directory and write it out.



I don't know PDP7 assembly either, but I don't see how it could stand for "previous directory" (and be equivalent to today's `cd -`). Any command to change directory needs to be done in-process by the shell, so it would need to be implemented as a builtin, not a standalone executable.

So "print directory" or (print something about the) "parent directory" seem more likely to me.


In the earliest versions of Unix, the shell didn't fork to run programs. It would exec the program, and then the program would exec the shell instead of exiting. Makes sense, right? That's what you'd do if you didn't have an operating system at all, and it's how, for example, CP/M worked. So you could write "cd" as a non-built-in command.

But it sounds like the other commenters have figured out that it's "pack directory".


After staring at the listing for a while, I think it is a kind of garbage collector for removing unlinked files from directory listings ("pack directory"?). As best as I can work out, the code does:

    open ..
    loop
        read a directory entry into tbuf
        if we read 0 bytes (eof presumably), break loop
        if tbuf[0] == '\0', go back to start of loop
        append tbuf to dir
    done
    close ..
    reopen .. with creat
    write the stuff we built up into dir to ..
    close ..
    exit


I think you're right. My C-ish translation (that ignores errors) is:

    int dst /*8*/, src /*9*/, c1, df, tbuf[8], dir[BIG];
    char dotdot[] = "..      ";

    df = open(dotdot, O_RDONLY);
    dst = dir - 1;
    while (read(df, tbuf, 8) != 0) {
      if (tbuf[0] != 0) {
        c1 = -8;
        src = tbuf - 1;
        do {
          *++dst = *++src;
        } while (++c1 != 0);
      }
    }
    close(df);

    df = creat(dotdot);
    write(df, dir, dst - dir + 1);
    close(df);
    exit();
That is, copy any directory entry with a non-zero inode into dir[], and then write that back. Pack Directory.

According to https://wiki.tuhs.org/doku.php?id=systems:pdp7_unix ‘..’ was the name of the current directory (i.e. what is now ‘.’), so this operated on the current directory.


Looking at this again, your psuedocode seems to be pretty reasonable. However, I'm still left wondering why this command would be necessary: doesn't it just read the directory entry into memory and write it back out again? I'm also curious why "sys write" takes .. directly when all the other calls seem to need dotdot.


Maurice Bach, "The Design of the UNIX Operating System", on page 73 describes the format of directory entries: https://archive.org/details/DesignOfTheUnixOperatingSystemBy...

"Directory entries may be empty, indicated by an inode number of 0".

So if the pseudo-code is correct, this may be an utility to squeeze out those empty entries.


If my reading is right, it filters out any with a leading null-byte before writing back out. My guess is that that is how they implemented unlink/rm - just write zeroes over the entry.

As for the sys write: my current hypothesis is that .. there is a placeholder for an argument which is written to by the preceding dac .+4. ('"If a program can't rewrite its own code", he asked, "what good is it?"'). But I can't make sense of what it's actually putting there - I'd guess length, but it looks like ~(dir - 2) + 8.


> but it looks like ~(dir - 2) + 8.

That took me a while. The ‘8’ in ‘tad 8' is the contents of memory location 8, i.e. the destination pointer in the memory copy, so it's ~(dir - 2) + dst. And since -x = ~x + 1 (there being no negation instruction), that works out to the length, dst - dir + 1.


It only opens dotdot once at the start, though, and calls creat(dotdot) later.


If dotdot can't be opened it fails. So I don't think creat was the same as what we now know.

It looks like the code opens dotdot, and then scans through it for something (match for dir?).

The creat call might mean "open for writing", and possibly the flags mean append. It appears dir is appended to the dotdot directory file.

So maybe, add this "dir" entry to dotdot if it isn't there already?

I can't guess what initializes dir; maybe it somehow comes as an argument from the command line or whatever.


dir appears to be copied into from tbuf at some point. It puts dir-1 into memory location 8 at the start, and then the code around the 2: section I'm pretty sure is a memcpy-ish loop like:

    c1 = -8;
    *9 = tbuf - 1;
    do {
        *(++(*8)) = *(++(*9));
    } while(++c1 != 0);
    goto 1b; // b for backwards?
It seems that memory locations 8-15 are auto-indexing[0], so the lac/dac i increments the pointed-at location before use, and isz is "increment and skip next instruction if zero".

[0] PDP7 manual, big PDF: http://bitsavers.trailing-edge.com/pdf/dec/pdp7/F-75P_PDP7pr...


Maybe it just prints the patent directory, then. I’m not sure why it calls creat, though…


https://github.com/DoctorWkt/pdp7-unix/blob/master/src/sys/N...

I think ‘dotdot’ is what is called ‘dd’ there (the corresponding symbol in ls is still named ‘dd'). I don't think the concept of a parent directory existed yet. Maybe ‘pd’ is something like ‘prepare directory’, constructing the dd/dotdot entry.


> Maybe ‘pd’ is something like ‘prepare directory’, constructing the dd/dotdot entry.

I think this is on the right track - it looks like the write call is to the newly created .., with df being used to hold the file descriptor.


But the program appears to fail if dotdot can't be opened for reading, and it appears to scan through it doing reads first. Then it closes and does a creat, which is expected to succeed also. I'm suspecting that at this stage of development, open may have been used for reading and creat for writing (including to an existing file).


From TFS¹, creat() on an existing file truncates it (i.e. to length zero, as O_TRUNC), and only the superuser can do this to a directory. open() has read/write flag bits as now.

I am leaning toward Pete_D's ‘pack directory’ idea.

¹ https://github.com/DoctorWkt/pdp7-unix/blob/e94417092a2980e7...




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: