/* * Lar - LAR format library file maintainer * by Stephen C. Hemminger * linus!sch or sch@Mitre-Bedford * * Heavily hacked for MS-DOS by Eric C. Brown * utah-cs!brownc or brownc@utah-cs * * Usage: lar key library [files] ... * * Key functions are: * u - Update, add files to library * t - Table of contents * e - Extract files from library * a - extract All files from library * p - Print files in library * d - Delete files in library * r - Reorganize library * Other keys: * v - Verbose * * This program is public domain software, no warranty intended or * implied. * * DESCRPTION * Lar is a MS-DOS program to manipulate program libraries. * The primary use of lar is to combine several files together * to reduce disk space used. * Lar maintains the date and time of files in the library, so * that Make can extract the date/time for rebuilding a file. * When files are restored, the file has the original date/time * rather than the date/time when the file was extracted. * The original CP/M library program LU is the product * of Gary P. Novosielski. * * PORTABILITY * The code is modeled after the Software tools archive program, * and is setup for Version 7 Unix. It does not make any assumptions * about byte ordering, explict and's and shift's are used. * If you have a dumber C compiler, you may have to recode new features * like structure assignment, typedef's and enumerated types. * * * Unix is a trademark of Bell Labs. * ** CP/M is a trademark of Digital Research. */ #include "lar.h" /* Globals */ char *fname[MAXFILES]; bool ftouched[MAXFILES]; struct ludir ldir[MAXFILES]; int errcnt, nfiles, nslots; bool verbose = false; char *getname(), *malloc(), *fgets(); long fcopy(), lseek(), rlwtol(); int update(), reorg(), table(), extract(), print(), delete(), getall(); typedef int (*PFI)(); main (argc, argv) int argc; char **argv; { register char *flagp; char *aname; /* name of library file */ PFI function = (PFI) NULL; /* function to do on library */ /* set the function to be performed, but detect conflicts */ #define setfunc(val) if(function != (PFI) NULL) conflict(); else function = val if (argc < 3) help (); aname = argv[2]; filenames (argc, argv); for(flagp = argv[1]; *flagp; flagp++) switch (*flagp) { case 'u': setfunc(update); break; case 't': setfunc(table); break; case 'e': setfunc(extract); break; case 'a': setfunc(getall); break; case 'p': setfunc(print); break; case 'd': setfunc(delete); break; case 'r': setfunc(reorg); break; case 'v': verbose = true; break; default: help (); } if(function == (PFI) NULL) { fputs("No function key letter specified\n", stderr); help(); } (*function)(aname); exit(0); } /* print error message and exit */ help () { fputs ("Usage: lar {uteapdr}[v] library [files] ...\n", stderr); fputs ("Functions are:\n\tu - Update, add files to library\n", stderr); fputs ("\tt - Table of contents\n", stderr); fputs ("\te - Extract files from library\n", stderr); fputs ("\ta - extract All files from library\n", stderr); fputs ("\tp - Print files in library\n", stderr); fputs ("\td - Delete files in library\n", stderr); fputs ("\tr - Reorganize library\n", stderr); fputs ("Flags are:\n\tv - Verbose\n", stderr); exit (1); } conflict() { fputs ("Conficting keys\n", stderr); help(); } /* * This ltoa doesn't call in the floating point library: * CI C86 does an sprintf! */ ltoa(val, buf) long val; register char *buf; { register int i; int j; char tbuf[20]; if (val == 0) { buf[0] = '0'; buf[1] = '\0'; return; } i = 19; while (val != 0) { tbuf[i--] = (val % 10) + '0'; val /= 10; } i++; for (j = 0; i <= 19; i++, j++) buf[j] = tbuf[i]; buf[j] = '\0'; } /* Get file names, check for dups, and initialize */ filenames (ac, av) int ac; char **av; { register int i, j=0; int k, loop; struct ffblk ff; bool iswild(); errcnt = 0; for (i = 0; i < ac - 3; i++) { if (iswild(av[i + 3])) { k = findfirst(av[i + 3], &ff, 0); while (k == 0) { fname[j] = malloc(strlen(ff.ff_name)+1); if (fname[j] == NULL) error("Out of core.."); strcpy(fname[j], ff.ff_name); for (loop = 0; ff.ff_name[loop] != 0; loop++) fname[j][loop] = tolower(ff.ff_name[loop]); ftouched[j] = false; if (j == MAXFILES) error ("Too many file names."); j++; k = findnext(&ff); } /* while */ } /* if-then */ else { /* not a wildcard */ fname[j] = av[i+3]; ftouched[j] = false; if (j == MAXFILES) error ("Too many file names."); j++; } /* else */ } /* for */ checkdups(j); } bool iswild(str) register char *str; { while (*str != '\0') { if (*str == '*' || *str == '?') return true; else str++; } return false; } checkdups(i) register int i; { register int j; fname[i] = NULL; nfiles = i; for (i = 0; i < nfiles; i++) for (j = i + 1; j < nfiles; j++) if (equal (fname[i], fname[j])) { fputs (fname[i], stderr); error (": duplicate file name"); } } table (lib) char *lib; { fildesc lfd; register int i; register struct ludir *lptr; long total, offset, size; int active = 0, unused = 0, deleted = 0, k; char *uname, buf[20]; if ((lfd = _open (lib, O_RDWR)) == SYS_ERROR) cant (lib); getdir (lfd); total = lwtol(ldir[0].l_len); if (verbose) { puts("Name Index Length"); fputs("Directory ", stdout); ltoa(total, buf); puts(buf); } for (i = 1, lptr = &ldir[1]; i < nslots; i++,lptr++) switch(lptr->l_stat) { case ACTIVE: active++; uname = getname(lptr->l_name, lptr->l_ext); if (filarg (uname)) if(verbose) { offset = lwtol(lptr->l_off); size = lwtol(lptr->l_len); fputs(uname, stdout); for (k = 1; k < 18 - strlen(uname); k++) fputc(' ', stdout); ltoa(offset, buf); fputs(buf, stdout); for (k = 1; k < 16 - strlen(buf); k++) fputc(' ', stdout); ltoa(size, buf); puts(buf, stdout); } else puts(uname); total += lwtol(lptr->l_len); break; case UNUSED: unused++; break; default: deleted++; } if(verbose) { puts("--------------------------------------"); fputs("Total bytes ", stdout); ltoa(total, buf); puts(buf); fputs("\nLibrary ", stdout); fputs(lib, stdout); fputs(" has ", stdout); itoa(nslots, buf); fputs(buf, stdout); fputs(" slots, ", stdout); itoa(deleted, buf); fputs(buf, stdout); fputs(" deleted ", stdout); itoa(active, buf); fputs(buf, stdout); fputs(" active, ", stdout); itoa(unused, buf); fputs(buf, stdout); puts(" unused"); } VOID _close (lfd); not_found (); } putdir (f) fildesc f; { lseek(f, 0L, 0); /* rewind f */ if (_write (f, (char *) ldir, DSIZE * nslots) != nslots * DSIZE) error ("Can't write directory - library may be botched"); } initdir (f) fildesc f; { register int i; long numbytes; char line[80]; static struct ludir blankentry = { UNUSED, { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }, { ' ', ' ', ' ' }, }; static struct ludir nameentry = { ACTIVE, { 'l', 'a', 'r', 'f', 'o', 'r', 'm', 't' }, { 'a', 'r', 'c' }, }; for (;;) { fputs ("Number of slots to allocate (1-255): ", stdout); if (fgets (line, 80, stdin) == NULL) error ("Eof when reading input"); nslots = atoi (line); if (nslots < 1) puts ("Must have at least one!"); else if (nslots > MAXFILES) puts ("Too many slots"); else break; } numbytes = nslots * DSIZE; for (i = 1; i < nslots; i++) ldir[i] = blankentry; ldir[0] = nameentry; ltolw (ldir[0].l_len, numbytes); ltolw (ldir[0].l_datetime, -1L); /* make funny date field */ putdir (f); } putname (cpmname, unixname) char *cpmname, *unixname; { register char *p1, *p2; for (p1 = unixname, p2 = cpmname; *p1; p1++, p2++) { while (*p1 == '.') { p2 = cpmname + 8; p1++; } if (p2 - cpmname < 11) *p2 = islower(*p1) ? toupper(*p1) : *p1; else { fputs (unixname, stderr); fputs (": name truncated\n", stderr); break; } } while (p2 - cpmname < 11) *p2++ = ' '; } /* filarg - check if name matches argument list */ filarg (name) char *name; { register int i; if (nfiles <= 0) return 1; for (i = 0; i < nfiles; i++) if (equal (name, fname[i])) { ftouched[i] = true; return 1; } return 0; } not_found () { register int i; for (i = 0; i < nfiles; i++) if (!ftouched[i]) { fputs(fname[i], stderr); fputs(": not in library.\n", stderr); errcnt++; } } extract(name) char *name; { getfiles(name, false); } print(name) char *name; { getfiles(name, true); } getall (libname) char *libname; { fildesc lfd, ofd; register int i; register struct ludir *lptr; union timer timeunion; extern int errno; char *unixname; if ((lfd = _open (libname, O_RDWR)) == SYS_ERROR) cant (libname); getdir (lfd); for (i = 1, lptr = &ldir[1]; i < nslots; i++, lptr++) { if(lptr->l_stat != ACTIVE) continue; unixname = getname (lptr->l_name, lptr->l_ext); fputs(unixname, stderr); ofd = _creat(unixname, 0); if (ofd == SYS_ERROR) { fputs (" - can't create", stderr); errcnt++; } else { VOID lseek (lfd, (long) lwtol (lptr->l_off), 0); acopy (lfd, ofd, lwtol (lptr->l_len)); timeunion.realtime = lwtol(lptr->l_datetime); VOID setftime(ofd, &(timeunion.ftimep)); VOID _close (ofd); } putc('\n', stderr); } VOID _close (lfd); not_found (); } getfiles (name, pflag) char *name; bool pflag; { fildesc lfd, ofd; register int i; register struct ludir *lptr; union timer timeunion; extern int errno; char *unixname; if ((lfd = _open (name, O_RDWR)) == SYS_ERROR) cant (name); ofd = pflag ? fileno(stdout) : SYS_ERROR; getdir (lfd); for (i = 1, lptr = &ldir[1]; i < nslots; i++, lptr++) { if(lptr->l_stat != ACTIVE) continue; unixname = getname (lptr->l_name, lptr->l_ext); if (!filarg (unixname)) continue; fputs(unixname, stderr); if (ofd != fileno(stdout)) ofd = _creat(unixname, 0); if (ofd == SYS_ERROR) { fputs (" - can't create", stderr); errcnt++; } else { VOID lseek (lfd, (long) lwtol (lptr->l_off), 0); acopy (lfd, ofd, lwtol (lptr->l_len)); timeunion.realtime = lwtol(lptr->l_datetime); if (ofd != fileno(stdout)) { VOID setftime(ofd, &(timeunion.ftimep)); VOID _close (ofd); } } putc('\n', stderr); } VOID _close (lfd); not_found (); } acopy (fdi, fdo, nbytes) /* copy nbytes from fdi to fdo */ fildesc fdi, fdo; long nbytes; { register int btr, retval; char blockbuf[BLOCK]; for (btr = (nbytes > BLOCK) ? BLOCK : (int) nbytes; btr > 0; nbytes -= BLOCK, btr = (nbytes > BLOCK) ? BLOCK : (int) nbytes) { if ((retval = _read(fdi, blockbuf, btr)) != btr) { if( retval == 0 ) { error("Premature EOF\n"); } if( retval == SYS_ERROR) error ("Can't read"); } if ((retval = _write(fdo, blockbuf, btr)) != btr) { if( retval == SYS_ERROR ) error ("Write Error"); } } } update (name) char *name; { fildesc lfd; register int i; if ((lfd = _open(name, O_RDWR)) == SYS_ERROR) { if ((lfd = _creat (name, 0)) == SYS_ERROR) { cant (name); } else initdir (lfd); } else getdir (lfd); /* read old directory */ if(verbose) fputs ("Updating files:\n", stderr); for (i = 0; i < nfiles; i++) addfil (fname[i], lfd); if (errcnt == 0) putdir (lfd); else fputs("fatal errors - library not changed\n", stderr); VOID _close (lfd); } addfil (name, lfd) char *name; fildesc lfd; { fildesc ifd; register int i; register struct ludir *lptr; long byteoffs, numbytes; union timer timeunion; if ((ifd = _open(name, O_RDWR)) == SYS_ERROR) { fputs("LAR: can't find ", stderr); fputs(name, stderr); fputs(" to add\n", stderr); errcnt++; return; } if(verbose) { fputs(name, stderr); fputs("\n", stderr); } for (i = 1, lptr = ldir+1; i < nslots; i++, lptr++) { if (equal( getname (lptr->l_name, lptr->l_ext), name) ) /* update */ break; if (lptr->l_stat != ACTIVE) break; } if (i >= nslots) { fputs(name, stderr); fputs(": can't add library is full\n",stderr); errcnt++; return; } lptr->l_stat = ACTIVE; putname (lptr->l_name, name); byteoffs = lseek(lfd, 0L, 2); /* append to end; return byte offset */ ltolw (lptr->l_off, byteoffs); numbytes = fcopy (ifd, lfd); ltolw (lptr->l_len, numbytes); getftime(ifd, &(timeunion.ftimep)); ltolw (lptr->l_datetime, timeunion.realtime); VOID _close (ifd); } long fcopy (ifd, ofd) /* copy file ifd (file) to ofd (library) */ fildesc ifd, ofd; { long total = 0L; register int n; char blockbuf[BLOCK]; while ( (n = _read(ifd, blockbuf, BLOCK)) > 0) { if (_write(ofd, blockbuf, n) != n) error("write error"); total += (long) n; } return total; } delete (lname) char *lname; { fildesc f; register int i; register struct ludir *lptr; if ((f = _open(lname, O_RDWR)) == SYS_ERROR) cant (lname); if (nfiles <= 0) error("delete by name only"); getdir (f); for (i = 0, lptr = ldir; i < nslots; i++, lptr++) { if (!filarg ( getname (lptr->l_name, lptr->l_ext))) continue; lptr->l_stat = DELETED; } not_found(); if (errcnt > 0) fputs ("errors - library not updated\n", stderr); else putdir (f); VOID _close (f); } reorg (name) char *name; { fildesc olib, nlib; int oldsize, k; register struct ludir *optr; register int i, j; struct ludir odir[MAXFILES], *nptr; char tmpname[80], buf[10]; char *mktemp(); strcpy(tmpname, mktemp("libXXXXXX")); if( (olib = _open(name, O_RDWR)) == SYS_ERROR) cant(name); if( (nlib = _creat(tmpname, 0)) == SYS_ERROR) cant(tmpname); getdir(olib); fputs("Old library has ", stdout); itoa(oldsize = nslots, buf); fputs(buf, stdout); puts(" slots."); /* copy ldir into odir */ for(i = 0, optr = ldir, nptr = odir ; i < nslots ; i++, optr++, nptr++) movmem((char *) optr, (char *) nptr, sizeof(struct ludir)); /* reinit ldir */ initdir(nlib); errcnt = 0; /* copy odir's files into ldir */ for (i = j = 1, optr = odir+1; i < oldsize; i++, optr++) { if( optr->l_stat == ACTIVE ) { if(verbose) { fputs("Copying: ", stderr); for (k = 0; k < 8; k++) fputc(optr->l_name[k], stderr); fputc('.', stderr); for (k = 0; k < 3; k++) fputc(optr->l_ext[k], stderr); fputc('\n', stderr); } copyentry( optr, olib, &ldir[j], nlib); if (++j >= nslots) { errcnt++; fputs("Not enough room in new library\n", stderr); break; } } } VOID _close(olib); putdir(nlib); VOID _close (nlib); if(errcnt == 0) { if (unlink(name) < 0 || rename(tmpname, name) < 0) { cant(name); exit(1); } } else fputs("Errors, library not updated\n", stderr); VOID unlink(tmpname); } copyentry( old, of, new, nf ) struct ludir *old, *new; fildesc of, nf; { long byteoffs, numbytes; register int btr; char blockbuf[BLOCK]; new->l_stat = ACTIVE; movmem(old->l_name, new->l_name, 8); /* copy name */ movmem(old->l_ext, new->l_ext, 3); /* copy extension */ numbytes = lwtol(old->l_datetime); /* copy date & time */ ltolw(new->l_datetime, numbytes); VOID lseek(of, (long) lwtol(old->l_off), 0); byteoffs = lseek(nf, 0L, 2); /* append to end; return new pos. */ ltolw (new->l_off, byteoffs); numbytes = lwtol(old->l_len); ltolw (new->l_len, numbytes); for (btr = (numbytes > BLOCK) ? BLOCK : (int) numbytes; btr > 0; numbytes -= BLOCK, btr = (numbytes > BLOCK) ? BLOCK : (int) numbytes ) { if (_read(of, blockbuf, btr) != btr) { error ("Read Error in CopyEntry"); } if (_write(nf, blockbuf, btr) != btr) { error ("Write Error in CopyEntry"); } } }