# ar --- file maintainer include "ar_def.r.i" include ARGUMENT_DEFS include AR_COMMON ARG_DECL character aname (MAXLINE) integer getarg string usage "Usage: ar -(a[d]|d|p|t|u[d]|x)[v] {}" PARSE_COMMAND_LINE ("adptuxv n"s, usage) if (getarg (1, aname, MAXLINE) == EOF) call error (usage) call delarg (1) # only file names are left Errcnt = 0 # no errors found so far call getfns if (Errcnt ~= 0) # errors encountered in fetching file names call error ("archive not altered"s) select when (ARG_PRESENT (a)) { if (ARG_PRESENT (p) || ARG_PRESENT (t) || ARG_PRESENT (u) || ARG_PRESENT (x)) call error (usage) # make sure p, t, u and x are absent call append (aname, ARG_PRESENT (d), ARG_PRESENT (v)) } when (ARG_PRESENT (t)) { # we know a is absent if (ARG_PRESENT (d) || ARG_PRESENT (p) || ARG_PRESENT (u) || ARG_PRESENT (x)) call error (usage) # make sure d, p, u and x are absent call table (aname, ARG_PRESENT (v)) } when (ARG_PRESENT (p)) { # we know t is absent if (ARG_PRESENT (d) || ARG_PRESENT (u) || ARG_PRESENT (x)) call error (usage) # make sure d, u and x are absent call extract (aname, PRINT, ARG_PRESENT (v)) } when (ARG_PRESENT (u)) { # we know p and t are absent if (ARG_PRESENT (x)) # make sure x is absent call error (usage) call update (aname, ARG_PRESENT (d), ARG_PRESENT (v)) } when (ARG_PRESENT (d)) { # we know p, t and u are absent if (ARG_PRESENT (x)) # make sure x is absent call error (usage) call delete (aname, ARG_PRESENT (v)) } when (ARG_PRESENT (x)) # we know d, p, t and u are absent call extract (aname, EXTRACT, ARG_PRESENT (v)) else # no command given call error (usage) stop end # acopy --- copy size words from ifd to ofd longint function acopy (ifd, ofd, size) filedes ifd, ofd longint size integer buf (MAXBUF), len integer readf longint i, lim lim = size - MAXBUF for (i = 0; i <= lim; i += len) { len = readf (buf, MAXBUF, ifd) if (len > 0) call writef (buf, len, ofd) else return (i) } if (size - i > 0) { # anything left? len = readf (buf, ints (size - i), ifd) if (len > 0) { call writef (buf, len, ofd) i += len } } return (i) end # addfil --- add file "name" to archive open on afd subroutine addfil (name, afd, errcnt) character name (ARB) filedes afd integer errcnt longint size longint fsize character head (MAXHEADER) filedes nfd filedes open nfd = open (name, READ) if (nfd == ERR) { call print (ERROUT, "*s: can't add*n"s, name) errcnt += 1 } if (errcnt == 0) { size = fsize (nfd) call makhdr (name, size, head) call putlin (head, afd) call flush$ (afd) call fcopy (nfd, afd) } call close (nfd) return end # amove --- move name1 to name2 subroutine amove (name1, name2) character name1 (ARB), name2 (ARB) filedes create, open filedes fd1, fd2 fd1 = open (name1, READ) if (fd1 ~= ERR) fd2 = create (name2, WRITE) if (fd1 == ERR || fd2 == ERR) { call print (ERROUT, "can't replace archive with *s*n"s, name1) call error (""s) } call fcopy (fd1, fd2) call close (fd1) call close (fd2) call remove (name1) return end # append --- add files to the end of an archive subroutine append (aname, remopt, verbose) character aname (ARB) bool remopt, verbose include AR_COMMON integer filarg, gethdr, remove integer i longint size filedes afd filedes open character head (MAXHEADER), name (NAMESIZE) if (Nfiles <= 0) return afd = open (aname, READWRITE) if (afd == ERR) call cant (aname) while (gethdr (afd, head, name, size) ~= EOF) { if (filarg (name) == YES) { call print (ERROUT, "*s: already in archive*n"s, name) Errcnt += 1 } call fskip (afd, size) } do i = 1, Nfiles if (Fstat (i) == NO) { if (verbose) call print (STDOUT, "*s*n"s, Fname (1, i)) call addfil (Fname (1, i), afd, Errcnt) } call close (afd) if (Errcnt ~= 0) call error ("archive not altered"s) else if (remopt) do i = 1, Nfiles if (remove (Fname (1, i)) == ERR) call print (ERROUT, "*s: can't remove*n"s, Fname (1, i)) return end # delete --- delete files from archive subroutine delete (aname, verbose) character aname (ARB) bool verbose include AR_COMMON integer afd, tfd integer create, open string tname "arctemp.=pid=" if (Nfiles <= 0) # protect innocents call error ("delete by name only"s) afd = open (aname, READWRITE) if (afd == ERR) call cant (aname) tfd = create (tname, READWRITE) if (tfd == ERR) call cant (tname) call replace (afd, tfd, DELETE, verbose, Errcnt) call close (afd) call close (tfd) if (Errcnt == 0) call notfound if (Errcnt == 0) call amove (tname, aname) else { call remove (tname) call error ("archive not altered"s) } return end # extract --- extract files from archive subroutine extract (aname, cmd, verbose) character aname (ARB), cmd bool verbose include AR_COMMON character ename (NAMESIZE), head (MAXHEADER) filedes afd, efd filedes open, create, mktemp integer filarg, gethdr, isatty longint size longint acopy afd = open (aname, READ) if (afd == ERR) call cant (aname) if (cmd == PRINT) if (isatty (STDOUT) == YES) { efd = mktemp (READWRITE) if (efd == ERR) call error ("can't open temporary file for extraction"p) } else efd = STDOUT while (gethdr (afd, head, ename, size) ~= EOF) if (filarg (ename) == NO) call fskip (afd, size) else { if (verbose) call print (STDOUT, "*s*n"s, ename) if (cmd ~= PRINT) efd = create (ename, WRITE) if (efd == ERR) { call print (ERROUT, "*s: can't create*n"s, ename) Errcnt += 1 call fskip (afd, size) } else { if (acopy (afd, efd, size) ~= size) { Errcnt += 1 call remark ("premature EOF"s) } if (cmd ~= PRINT) call close (efd) } } if (cmd == PRINT && isatty (STDOUT) == YES) { call rewind (efd) call fcopy (efd, STDOUT) call rmtemp (efd) } if (Errcnt == 0) call notfound return end # filarg --- check if name matches argument list integer function filarg (name) character name (ARB) include AR_COMMON integer i integer equal if (Nfiles <= 0) return (YES) do i = 1, Nfiles if (equal (name, Fname (1, i)) == YES) { Fstat (i) = YES # mark it visited return (YES) } return (NO) end # fsize --- return size of file in words longint function fsize (fd) filedes fd longint markf call wind (fd) fsize = markf (fd) call rewind (fd) return end # fskip --- skip n words on file fd subroutine fskip (fd, n) filedes fd longint n call seekf (n, fd, REL) return end # getfns --- get file names into fname, check for duplicates subroutine getfns include AR_COMMON character junk (NAMESIZE) integer i, j, state (4) integer equal, gfnarg, getarg if (getarg (1, junk, 1) == EOF) { # no files specified Nfiles = 0 return } state (1) = 1 for (i = 1; i <= MAXFILES; i += 1) { Fstat (i) = NO if (gfnarg (Fname (1, i), state) == EOF) break } if (i > MAXFILES && gfnarg (junk, state) ~= EOF) { call print (ERROUT, "can't handle more than *i file names*n"s, MAXFILES) Errcnt += 1 } Nfiles = i - 1 for (i = 1; i < Nfiles; i += 1) for (j = i + 1; j <= Nfiles; j += 1) if (equal (Fname (1, i), Fname (1, j)) == YES) { call print (ERROUT, "*s: duplicate file name*n"s, Fname (1, i)) Errcnt += 1 } return end # gethdr --- get header info from fd integer function gethdr (fd, head, name, size) filedes fd character head (MAXHEADER), name (NAMESIZE) longint size include AR_COMMON character temp (NAMESIZE) integer i, len integer equal, getlin, getwrd longint ctol string hdr HEADER if (getlin (head, fd, MAXHEADER) == EOF) return (EOF) i = 1 len = getwrd (head, i, temp, NAMESIZE) if (equal (temp, hdr) == NO && equal (temp, "###H:"s) == NO) { call remark ("archive not in proper format"s) Errcnt += 1 return (EOF) } if (equal (temp, "###H:"s) == YES) call convert_header (head) call getwrd (head, i, name, NAMESIZE) size = ctol (head, i) return (OK) end # getwrd --- get non-blank word from in (i) into out, increment i integer function getwrd (in, i, out, max) integer in (ARB), out (ARB) integer i, max integer j SKIPBL (in, i) j = 1 while (in (i) ~= EOS && in (i) ~= ' 'c && in (i) ~= NEWLINE) { if (j >= max) break out (j) = in (i) i += 1 j += 1 } out (j) = EOS return (j - 1) end # get_date --- construct the current date for page heading subroutine get_date (sdate) character sdate (12) integer month, i integer ctoi character mmddyy (9) string_table spos, smon "Jan"/ "Feb"/ "Mar"/ "Apr"/ "May"/ "Jun"/ "Jul"/ "Aug"/ "Sep"/ "Oct"/ "Nov"/ "Dec" call date (SYS_DATE, mmddyy) i = 1 month = ctoi (mmddyy, i) call encode (sdate, 12, "*s-*,2s-19*,2s"s, smon (spos (month + 1)), mmddyy (4), mmddyy (7)) return end # makhdr --- make header line for archive member subroutine makhdr (name, size, head) character name (NAMESIZE), head (MAXHEADER) longint size character dat (12), tim (9) string hdr HEADER call get_date (dat) call date (SYS_TIME, tim) call encode (head, MAXHEADER, "*s *s *l *s *s*n"s, hdr, name, size, dat, tim) return end # notfound --- print "not found" message subroutine notfound include AR_COMMON integer i for (i = 1; i <= Nfiles; i += 1) if (Fstat (i) == NO) { call print (ERROUT, "*s: not in archive*n"s, Fname (1, i)) Errcnt += 1 } return end # replace --- replace or delete files subroutine replace (afd, tfd, cmd, verbose, errcnt) filedes afd, tfd character cmd bool verbose integer errcnt character head (MAXHEADER), name (NAMESIZE) integer filarg, gethdr longint size longint acopy filedes tfd while (gethdr (afd, head, name, size) ~= EOF) if (filarg (name) == YES) { if (verbose) call print (STDOUT, "*s*n"s, name) if (cmd == UPDATE) # add new one call addfil (name, tfd, errcnt) call fskip (afd, size) # discard old one } else { call putlin (head, tfd) if (acopy (afd, tfd, size) ~= size) { errcnt += 1 call remark ("premature EOF"s) } } return end # table --- print table of archive contents subroutine table (aname, verbose) character aname (ARB) logical verbose include AR_COMMON character head (MAXHEADER), name (NAMESIZE) integer filarg, gethdr longint size filedes afd filedes open afd = open (aname, READ) if (afd == ERR) call cant (aname) while (gethdr (afd, head, name, size) ~= EOF) { if (filarg (name) == YES) call tprint (head, verbose) call fskip (afd, size) } if (Errcnt == 0) call notfound return end # tprint --- print table entry for one member subroutine tprint (buf, verbose) character buf (ARB) logical verbose integer i character name (NAMESIZE), size (12), date (12), time (9) i = 1 call getwrd (buf, i, name, NAMESIZE) # discard header flag call getwrd (buf, i, name, NAMESIZE) # get member name if (~ verbose) # only want name call print (STDOUT, "*s*n"s, name) else { call getwrd (buf, i, size, 12) call getwrd (buf, i, date, 12) call getwrd (buf, i, time, 9) call print (STDOUT, "*s *s *-6s *s*n"s, date, time, size, name) } return end # update --- update existing files, add new ones at end subroutine update (aname, remopt, verbose) character aname (ARB) bool remopt, verbose include AR_COMMON integer i integer remove filedes afd, tfd filedes open, create string tname "arctemp.=pid=" afd = open (aname, READWRITE) if (afd == ERR) # maybe it's a new one afd = create (aname, READWRITE) if (afd == ERR) call cant (aname) tfd = create (tname, READWRITE) if (tfd == ERR) call cant (tname) call replace (afd, tfd, UPDATE, verbose, Errcnt) # update existing for (i = 1; i <= Nfiles; i += 1) # add new ones if (Fstat (i) == NO) { if (verbose) call print (STDOUT, "*s*n"s, Fname (1, i)) call addfil (Fname (1, i), tfd, Errcnt) Fstat (i) = YES } call close (afd) call close (tfd) if (Errcnt == 0) { call amove (tname, aname) if (remopt) # remove files after updating archive? for (i = 1; i <= Nfiles; i += 1) if (remove (Fname (1, i)) == ERR) call print (ERROUT, "*s: can't remove*n"s, Fname (1, i)) } else { call remove (tname) call error ("archive not altered"s) } return end # convert_header --- put the header into the new format subroutine convert_header (head) character head (MAXHEADER) integer i integer getwrd character name (NAMESIZE), size (12), mon (4), day (3), year (5), time (9) string flag HEADER i = 1 call getwrd (head, i, name, NAMESIZE) # skip header flag call getwrd (head, i, name, NAMESIZE) # get member name call getwrd (head, i, size, 12) # get member size call getwrd (head, i, mon, 4) # get month call getwrd (head, i, day, 3) # get day call getwrd (head, i, year, 5) # get year call getwrd (head, i, time, 9) # get time call encode (head, MAXHEADER, "*s *s *s *s-*s-*s *s*n"s, flag, name, size, mon, day, year, time) return end