/* * Lar - LU format library file maintainer * by Stephen C. Hemminger * Bedford MA * * DeSmet version T. Bonfield Feb 84 * DeSmet updates R. McVay Mar 84 * * DESCRIPTION * Lar is a program to manipulate CP/M LU format libraries. * The original CP/M library program LU is the product * of Gary P. Novosielski. The primary use of lar is to combine several * files together for upload/download to a personal computer. * * Usage: lar [-]key library [files] ... * * Key functions are: * a - Add files to library (also creates new libraries) * l - List directory of library * e - Extract files from library * p - Print files in library * d - Delete files in library * r - Reorganize library * * EXAMPLES: * lar l foo.lbr list all files in FOO.LBR * lar e foo.lbr 1.c 2.c extract files 1.c, 2.c from FOO.LBR * lar p foo.lbr 1.c display 1.c from FOO.LBR * lar a foo.lbr 1.c 2.c 3.c add or replace files in FOO.LBR * * When creating a new library, you will be prompted for the maximum * number of entries it can contain. Assuming NEW.LBR doen't exist ... * lar a new.lbr create an empty library * lar a new.lbr a.c,b.c,d.c create NEW.LBR, add files. * * The Reorganize option causes .tmp to be created, and * the contents of the old library to be copied into it. * * This program is public domain software, no warranty intended or * implied. * * * PORTABILITY * The original version by Stephan C. Hemminger was set up for * Version 7 UNIX, and was not useable as is. It has been hacked * to fit the DeSmet C compiler for MSDOS. The basic * problems were: fread() and fwrite() incompatibility, no * text/binary differentiation problem in MSDOS. * * Also, I have made random changes to the source merely to reflect * my programming taste; he original code was quite good. I have also * changed the wording of some errors, etc more in line with the current * flavor of messages in the micro environment. The original Verbose * flag option was removed, and made the default. No need to suppress * what few messages and text there is. * * As mentioned before, ther is no problem with text or binary files; * they are treated identically. Control-Z characters are added to the * end of all files to round it up to a multiple of 128 bytes. * * Note that all files are kept as multiples of 128 bytes, to be * compatible with the CP/M utility LU. This may present a problem * with certain data files, but will be OK for text and .COM files * anyways, and probably most other files. * T. Bonfield * * v1.1- added the exception handler for a "dashed" option and a default * library extension of .lbr. * - fixed a bug that put the drive descriptor into the library file * entry. * v1.2- fixed a "member not found" bug by forcing all filenames to lower * case. * - changed the commands u->a and t->l to jive with LU better. * v1.3- fixed subtle "casting" bug in copyentry(). * - copyentry() now uses acopy(). * - rewrote fcopy() and acopy() to use 16K copy buffer to * eliminate disk thrashing on -a -e & -r. * - fixed "not enough room" bug in reorg(). * R. McVay * * * Unix is a trademark of Bell Labs. * ** CP/M is a trademark of Digital Research. */ #include /* Library file status values: */ #define ACTIVE 0 #define UNUSED 0xff #define DELETED 0xfe #define CTRLZ 0x1a #define MAXFILES 256 #define SECTOR 128 #define BIGBUFF 16384 #define DSIZE (sizeof(struct ludir)) #define SLOTS_SEC (SECTOR/DSIZE) #define equal(s1, s2) ( strcmp(s1,s2) == 0 ) #define false 0 #define true 1 #define bool int /* Globals */ char *fname[MAXFILES]; bool ftouched[MAXFILES]; struct ludir { /* internal dir. stucture, 32 bytes */ char l_stat; /* 12 byte filename: */ char l_name[8]; char l_ext[3]; int l_off; /* offset in library, */ int l_len; /* length of file, */ int l_fill[8]; /* 16 byte filler, */ } ldir[MAXFILES]; int errcnt, nfiles, nslots; char *getname(), *sprintf(), *strlower(), *malloc(); long lseek(); main (argc, argv) int argc; char *argv[]; { char *flagp, aname[20], *request; if (argc < 3) help (); if (*argv[1] == '-') /* strip the dash if present .rlm */ ++argv[1]; strcpy(aname, argv[2]); /* name of LBR file, */ if (index(aname, ".") == -1) /* add default extension .lbr .rlm */ strcat(aname, ".lbr"); filenames (argc, argv); switch (tolower(*argv[1])) { case 'a': update(aname); break; case 'l': table(aname); break; case 'e': getfiles (aname, false); break; case 'p': getfiles (aname, true); break; case 'd': delete(aname); break; case 'r': reorg(aname); break; default: help(); } exit(); } help() /* print error message and exit */ { printf("LAR v1.3 - 17 Mar 1984\n"); printf("\nUsage: lar [-] library [files] ...\n"); printf("\nFunctions are:\n\ta - Add files to library\n"); printf("\tl - List library directory\n"); printf("\te - Extract files from library\n"); printf("\td - Delete files in library\n"); printf("\tr - Reorganize library\n"); printf("\tp - Print files in library\n"); printf("\nAssumed library extension is .LBR\n"); exit (1); } index(s, t) /* return index of t in s, -1 if none */ char *s, *t; { int i,j,k; for (i = 0; s[i] != '\0'; i++) { for (j=i, k=0; t[k]!='\0' && s[j]==t[k]; j++, k++) ; if (t[k] == '\0') return(i); } return(-1); } error (str) char *str; { printf("LAR: %s\n", str); exit (1); } cant (name) char *name; { printf("%s: File open error\n", name); exit (1); } filenames (ac, av) /* Get file names, check for dups, and initialize */ int ac; char *av[]; { int i, j; errcnt = 0; for (i = 0; i < ac - 3; i++) { fname[i] = strlower(av[i + 3]); ftouched[i] = false; if (i == MAXFILES) error ("Too many file names."); } fname[i] = NULL; nfiles = i; for (i = 0; i < nfiles; i++) for (j = i + 1; j < nfiles; j++) if (equal(fname[i], fname[j])) { printf("%s ", fname[i]); error (": duplicate file name"); } } table (lib) char *lib; { FILE lfd; int i, total; int active = 0, unused = 0, deleted = 0; char *uname; if ((lfd= open(lib,2)) == -1) cant (lib); getdir (lfd); total = ldir[0].l_len; printf("Name Index Length (128 byte blocks)\n"); printf("Directory %4u %6u\n", 0, total); for (i = 1; i < nslots; i++) switch(ldir[i].l_stat) { case ACTIVE: active++; uname = getname(&ldir[i].l_name, &ldir[i].l_ext); total += ldir[i].l_len; printf("%-12s %4u %7u\n", uname,ldir[i].l_off,ldir[i].l_len); break; case UNUSED: unused++; break; default: deleted++; } printf("-----------------------------\n"); printf("Total blocks %7u\n", total); printf("\nLibrary %s has %u slots, %u deleted, %u active, %u unused\n", lib, nslots, deleted, active, unused); close (lfd); not_found (); } getdir (f) FILE f; { int cnt; lseek(f,0L,0); if (read(f,&ldir[0],DSIZE) != DSIZE) /* read 1st entry to find */ error ("No directory\n"); /* number of slots, */ nslots = ldir[0].l_len * SLOTS_SEC; cnt= DSIZE * (nslots - 1); /* already read one slot, */ if (read(f,&ldir[1],cnt) != cnt) error ("Can't read directory - is it a library?"); } putdir (f) FILE f; { lseek(f,0L,0); if (write(f,&ldir,nslots * DSIZE) != (nslots * DSIZE)) error ("Can't write directory - library may be botched"); } initdir (f) FILE f; { int i; int numsecs; char line[80]; for (;;) { puts ("Number of slots to allocate: "); gets(line); puts("\r\n"); nslots = atoi (line); if (nslots < 1) printf("Must have at least one!\n"); else if (nslots > MAXFILES) printf ("Too many slots\n"); else break; } numsecs = nslots / SLOTS_SEC; if (nslots != numsecs * SLOTS_SEC) ++numsecs; nslots = numsecs * SLOTS_SEC; for (i = 0; i < nslots; i++) { ldir[i].l_stat= UNUSED; blank_fill(&ldir[i].l_name,8); blank_fill(&ldir[i].l_ext,3); } ldir[0].l_stat = ACTIVE; ldir[0].l_len= numsecs; putdir (f); } blank_fill(s,n) /* Fill an array with blanks, no trailing null. */ char *s; int n; { while (n--) *s++= ' '; } char *getname(nm, ex) /* convert nm.ex to a Unix style string */ char *nm, *ex; { static char namebuf[14]; int i,j; for (i= 0; (i < 8) && (nm[i] != ' '); i++) namebuf[i]= tolower(nm[i]); j= i; namebuf[j++]= '.'; for (i= 0; (i < 3) && (ex[i] != ' '); i++) namebuf[j++]= tolower(ex[i]); namebuf[j]= '\0'; return namebuf; } filarg (name) /* filarg - check if name matches argument list */ 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; } char *strlower(string) /* tolower() all characters in string .rlm */ char *string; { char *ptr; ptr = string; while (*string) { *string = tolower(*string); ++string; } return(ptr); } not_found() { register int i; for (i = 0; i < nfiles; i++) if (!ftouched[i]) { printf("%s : not in library.\n", fname[i]); errcnt++; } } getfiles (name, pflag) char *name; bool pflag; { FILE lfd, ofd; int i; char *unixname; if ((lfd= open(name,2)) == -1) cant (name); getdir (lfd); for (i = 1; i < nslots; i++) { if(ldir[i].l_stat != ACTIVE) continue; unixname = getname(&ldir[i].l_name, &ldir[i].l_ext); if (!filarg (unixname)) continue; printf("Extracting %s\n", unixname); if (pflag) ofd= open("CON:",2); else ofd= creat(unixname); if (ofd == -1) { printf("%s - can't create output file\n",unixname); errcnt++; } else { lseek (lfd, (long) ldir[i].l_off * SECTOR,0); acopy (lfd, ofd, ldir[i].l_len); if (!pflag) close (ofd); } } close (lfd); not_found (); } acopy (fdi, fdo, nsecs) FILE fdi, fdo; unsigned nsecs; { char *buf; int n; if ((buf = malloc(BIGBUFF)) == 0) error("acopy: no buffer"); do { if ((n = read(fdi, buf, nsecs*SECTOR)) == -1) error("acopy: read error"); if (write(fdo, buf, n) != n) error("acopy: write error"); nsecs -= n/SECTOR; } while (nsecs); free(buf); } update (name) char *name; { FILE lfd; register int i; if ((lfd = open (name,2)) == -1) { if ((lfd = creat(name)) == -1) cant (name); initdir (lfd); } getdir (lfd); /* read directory, */ for (i = 0; (i < nfiles) && (errcnt == 0); i++) addfil (fname[i], lfd); if (errcnt != 0) printf("fatal errrs - last file may be bad\n"); putdir (lfd); close (lfd); } addfil (name, lfd) char *name; FILE lfd; { FILE ifd; register int secoffs, numsecs; register int i; if ((ifd= open(name,2)) == -1) { printf("%s: can't find to add\n",name); errcnt++; return; } for (i = 0; i < nslots; i++) { if (equal( getname(&ldir[i].l_name, &ldir[i].l_ext), name) ) { printf("Updating existing file %s\n",name); break; } if (ldir[i].l_stat != ACTIVE) { printf("Adding new file %s\n",name); break; } } if (i >= nslots) { printf("Can't add %s, library is full\n",name); errcnt++; return; } ldir[i].l_stat = ACTIVE; cvt_to_fcb (name, &ldir[i].l_name); /* append to end */ secoffs = lseek(lfd, 0L, 2) / SECTOR; ldir[i].l_off= secoffs; numsecs = fcopy (ifd, lfd); ldir[i].l_len= numsecs; close (ifd); } fcopy (ifd, ofd) FILE ifd, ofd; { int total = 0; int n; char *sectorbuf; if ((sectorbuf = malloc(BIGBUFF)) == 0) error("fcopy: no buffer"); do { if ((n = read(ifd, sectorbuf, BIGBUFF)) == -1) error("fcopy: read error"); while (n % SECTOR) sectorbuf[n++] = CTRLZ; if (write(ofd, sectorbuf, n) != n) error("fcopy: write error"); total += n/SECTOR; } while (n == BIGBUFF); free(sectorbuf); return total; } delete (lname) char *lname; { FILE f; int i; if ((f= open(lname,2)) == -1) cant (lname); if (nfiles <= 0) error("delete by name only"); getdir (f); for (i = 0; i < nslots; i++) { if (!filarg ( getname(&ldir[i].l_name, &ldir[i].l_ext))) continue; ldir[i].l_stat = DELETED; } not_found(); if (errcnt > 0) printf("errors - library not updated\n"); else putdir (f); close (f); } reorg (name) char *name; { FILE olib, nlib; int oldsize; register int i, j; struct ludir odir[MAXFILES]; char tmpname[SECTOR]; for (i= 0; (i < 8) && (name[i] != '.'); i++) /* copy filename, */ tmpname[i]= name[i]; /* strip off extention, */ tmpname[i]= '\0'; strcat(tmpname,".tmp"); /* make new name, */ if ((olib= open(name,2)) == -1) cant(name); if ((nlib= creat(tmpname)) == -1) cant(tmpname); getdir(olib); printf("Old library has %d slots\n", oldsize = nslots); for(i = 0; i < nslots ; i++) copymem( (char *) &odir[i], (char *) &ldir[i], sizeof(struct ludir)); initdir(nlib); errcnt = 0; for (i = j = 1; i < oldsize; i++) if( odir[i].l_stat == ACTIVE ) { printf("Copying: %-8.8s.%3.3s\n",&odir[i].l_name, &odir[i].l_ext); copyentry( &odir[i], olib, &ldir[j], nlib); if (++j > nslots) { errcnt++; printf("Not enough room in new library\n"); break; } } close(olib); putdir(nlib); close (nlib); if (errcnt == 0) { unlink(name); /* delete orig file, */ rename(tmpname,name); /* rename it, */ } else { printf("Errors, library not updated\n"); unlink(tmpname); } } copyentry(old, of, new, nf) struct ludir *old, *new; FILE of, nf; { int secoffs, numsecs; new->l_stat = ACTIVE; copymem(&new->l_name, &old->l_name, 8); copymem(&new->l_ext, &old->l_ext, 3); lseek(of, (long) old->l_off * SECTOR, 0); /* home of subtle bug */ secoffs = lseek(nf, 0L, 2) / SECTOR; new->l_off= secoffs; numsecs = old->l_len; new->l_len= numsecs; acopy(of, nf, numsecs); } copymem(dst, src, n) char *dst, *src; unsigned n; { while(n-- != 0) *dst++ = *src++; } /* Convert a normal asciiz string to MSDOS/CPM FCB format. Make the filename portion 8 characters, extention 3 maximum. */ cvt_to_fcb(inname,outname) char *inname; char outname[]; { char c; int i; for (i= 0; i < 11; i++) outname[i]= ' '; /* clear out name, */ inname += index(inname, ":") + 1; /* remove drive descriptor .rlm */ for (i= 0; i < 11; i++) { if (*inname == '\0') /* if null, */ outname[i]= ' '; /* pad with blanks, */ else if (*inname == '.') { /* if a ot, */ ++inname; /* skip it, */ i= 7; /* skip to extention, */ } else { outname[i]= toupper(*inname); ++inname; } } return; }