Documentation
pdp11-xstr — user guide
pdp11-xstr is Bill Joy's 1978 xstr: it reads a C program, pulls its string literals out into one shared, read-only data file (strings), and rewrites the program so each literal becomes a reference (&xstr[N]) into that po…
pdp11-xstr — user guide
pdp11-xstr is Bill Joy's 1978 xstr: it reads a C program, pulls its string
literals out into one shared, read-only data file (strings), and rewrites the
program so each literal becomes a reference (&xstr[N]) into that pool. Identical
strings are stored once — and a string that is a trailing substring of another
is not stored at all — which is how it saved text and data space on the memory-
constrained PDP-11. It runs on your host as part of the 2.9BSD build.
For how it works inside — the scan, the hashing, the pool layout — see the design document.
1. Synopsis
pdp11-xstr [ -v ] [ -c ] [ - ] [ name ... ]
(The tool's own usage line still spells itself xstr.) Each name is a C source
file; a bare - reads the program from standard input instead.
xstr is normally used in two phases, in place of the compile step:
xstr -c foo.c # per source: append its strings to the pool, rewrite -> x.c
cc -c x.c # compile the rewritten source
mv x.o foo.o
... # repeat -c for every source
xstr # once at the end: emit xs.c from the finished pool
cc -c xs.c # compile the shared pool, link xs.o with the rest
Given a name without -c, it runs one-shot instead: a private pool in
/tmp (removed afterward) and both x.c and xs.c for that single file.
2. Options
| option | meaning |
|---|---|
-c |
compile mode: seed from and append to the persistent strings pool, rewrite the source to x.c, but do not emit xs.c (the final bare xstr does that) |
- |
read the program from standard input instead of a named file |
-v |
verbose: report each string on stderr as new at N: or found at N: (with the string, control bytes shown as ^X, high bytes as \ooo) |
Flags may be glued together (xstr -cv foo.c). An unrecognised flag prints the
usage line to stderr but is otherwise ignored — the run continues.
With no operands at all (xstr), the tool seeds from the existing strings
pool, processes nothing, and writes xs.c — this is the closing phase of the
two-step build.
3. Files it produces
All paths are relative to the current directory unless noted.
x.c— the rewritten source: the input C with every string literal replaced by(&xstr[N])and achar xstr[];reference added at the top. Reopened fresh for each input, so it holds the most recently processed file.strings— the shared string pool: the decoded literal bytes, each NUL-terminated, packed so identical and suffix-sharing strings coincide. It persists in the current directory and accumulates across-cruns. (When anameis given without-c, the pool is instead a temporary/tmp/xstrXXXXXXthat is deleted on exit.)xs.c— the pool as C source:char xstr[] = { 0x.., … };. Compile and link this once with every object built from anx.c. Not written under-c.
An interrupt (or a write error) removes any temporary pool, x.c, and xs.c
before exiting, so a killed run leaves nothing half-written behind.
4. Exit status
- 0 — success.
- 1 — cannot create
x.c. - 2 — cannot open a named input file.
- 3 — read error on the input.
- 4 — write error on the
stringspool. - 5 — cannot reopen
stringsto buildxs.c. - 6 — cannot create
xs.c. - 7 — interrupted, or an output error mid-run (temporaries cleaned up first).
- 8 — cannot open
stringsfor writing.
5. Examples
xstr -c foo.c # phase 1: extract foo.c's strings, rewrite to x.c
xstr # phase 2: build xs.c from the accumulated pool
xstr foo.c # one-shot: x.c and xs.c for foo.c, no persistent pool
cat foo.c | xstr - # same, reading the program from standard input
xstr -v -c foo.c # narrate each string as it is added to the pool
A full 2.9BSD-style build of several sources:
rm -f strings
for f in *.c; do xstr -c $f && cc -c x.c && mv x.o ${f%.c}.o; done
xstr && cc -c xs.c # then link every .o together with xs.o
Continue to the design document for the pool layout and the suffix-overlap hashing.