cp -t Unix 5th Edition

root@openindiana:~# zpool detach mypool c4t0d1p2


root@openindiana:~# zpool list


NAME     SIZE  ALLOC   FREE    CAP  DEDUP  HEALTH  ALTROOT


mypool   832G   112K   832G     0%  1.00x  ONLINE  -


root@openindiana:~# zpool status


 pool: mypool


state: ONLINE


 scan: none requested


config:


NAME        STATE     READ WRITE CKSUM


mypool      ONLINE       0     0     0


  c4t0d0p2  ONLINE       0     0     0


errors: No known data errors



While reading this marvelous retrospective,

A Research UNIX Reader:

Annotated Excerpts from the Programmer’s Manual,

1971-1986

M. Douglas McIlroy

http://www.cs.dartmouth.edu/~doug/reader.pdf

I came upon this passage:

CP (v1 page 17, v5 page 18)

The war-horse utility cp and its close relative mv originally worked on lists of pairs. Such lists, howev er, could not be generated by the shell’s * convention. All too often mistyped lists clobbered precious files. Consequently both utilities were promptly cut back to handle just one from-to pair (v2). At the same time mv was generalized to move files to a named directory. Strangely cp picked up only a BUG note suggesting the feature. By the time of v3 both had converged to their present forms, although an unexplained option -t intruded briefly in v5. What seems natural in hindsight was not clear cut at the time: the final conventions arose only after long discussions about how properly to handle file permissions and multiple files. In fact the discussion is not yet closed. Whether and how to recurse on directories is still debated: v7, v8, and v9 each offered a different way to do it.

Ever the "Human manual page", I was quite curious what had "cp -t" done. So off to the Version 5 Unix manual.

ftp://gcu-squad.org/mirrors/tuhs/PDP-11/Distributions/research/Dennis_v5/v5man.pdf#page=43

Okay No one is quite sure what the flag -t does. That gets me even more curious!

Luckily we can do some code archaeology and look at the source code for Research Unix 5th Edition from

The Unix Heritage Society

http://minnie.tuhs.org/cgi-bin/utree.pl?file=V5/usr/source/s1/cp.c

Looking at the code '-t' has the signs of code just sort shoved in. It looks from the variable 'tell', that on successfully completing the copy, if the '-t' flag was used, the count of 512 bytes buffers used was written to standard output with a newline.
This is very sim
ilar to the querying the progress of OpenVMS BACKUP using the Control-T key.

The reason it probably was obscure is that it looks to have been broken during an edit.

The conf(n,width,buf) composes in the buf of length width the characters representing n recursively from right to left.

The gotcha occurs in main. Most likely the maximum number of digits was 3 and someone decided to expand this limit to 6.

The problem was they missed the middle line in main's code for 'tell' being set: buf[3]='\n';

That magic number which threw a newline, '\n' at the end of the string, was missed.

The result would mean overwriting a digit in the total with a newline, and the rest of the digits being left without a following newline.

/*

* cp oldfile newfile

*/


main(argc,argv)

char **argv;

{

int buf[256];

int fold, fnew, n, ct, tell;

char *p1, *p2, *bp;

int mode;


tell = 0;

if(argc == 4 && argv[1][0] == '-' && argv[1][1] == 't') {

argc--;

argv++;

tell = 1;

}

if(argc != 3) {

write(1, "Usage: cp oldfile newfile\n", 26);

exit(1);

}

/* [ ... source code removed for "brevity" ... ] */

while(n = read(fold, buf, 512)) {

if(n < 0) {

write(1, "Read error\n", 11);

exit(1);

} else

if(write(fnew, buf, n) != n){

write(1, "Write error.\n", 13);

exit(1);

}

ct++;

}

if(tell) { /* [Probably the number of digits originally 3, and was expanded to 6 but with a bug!] */

conf(ct, 6, buf); /* [... This looks like an update bug! I think this was changed from 3 to 6 ...]*/

buf[3] = '\n'; /* [...but this 3 wasn't updated to 7 ...]*/

write(1, buf, 7); /* [...And this 7 was probably a 4 that got changed to 7 ...] */

}

exit(0);

}
conf(n,width,buf)

char *buf;

{

auto i,a;


i = width;

while(i--) buf[i] = ' '; /* [ fill the buffer with spaces ] */


/* [ This looks to print the count of 512 bytes blocks of characters written successfully in main() ] */

/* [ It recursively composes the digits in the buffer from right to left ] */

buf[(a = n/10)?conf(a,--width,buf):--width] = n%10 + '0';


return(++width);

}