/* * Lar - LU format library file maintainer * by Stephen C. Hemminger * linus!sch or sch@Mitre-Bedford * * Usage: lar key library [files] ... * * Key functions are: * u - Update, add files to library * t - Table of contents * e - Extract files from library * p - Print files in library * d - Delete files in library * r - Reorginize library * Other keys: * v - Verbose * * This program is public domain software, no warranty intended or * implied. * * DESCRPTION * Lar is a Unix 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. * * 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. * * BUGS/MISFEATURES * The biggest problem is text files, the programs tries to detect * text files vs. binaries by checking for non-Ascii (8th bit set) chars. * If the file is text then it will throw away Control-Z chars which * CP/M puts on the end. All files in library are padded with Control-Z * at the end to the CP/M sector size if necessary. * * No effort is made to handle the difference between CP/M and Unix * end of line chars. CP/M uses Cr/Lf and Unix just uses Lf. * The solution is just to use the Unix command sed when necessary. * * * Unix is a trademark of Bell Labs. * ** CP/M is a trademark of Digital Research. */ #include #include #define ACTIVE 00 #define UNUSED 0xff #define DELETED 0xfe #define CTRLZ 0x1a #define MAXFILES 256 #define SECTOR 128 #define DSIZE ( sizeof(struct ludir) ) #define SLOTS_SEC (SECTOR/DSIZE) #define equal(s1, s2) ( strcmp(s1,s2) == 0 ) /* if you don't have void type just define as blank */ #define VOID (void) /* if no enum's then define false as 0 and true as 1 and bool as int */ typedef enum {false=0, true=1} bool; /* Globals */ char *fname[MAXFILES]; bool ftouched[MAXFILES]; typedef struct { unsigned char lobyte; unsigned char hibyte; } word; /* convert word to int */ #define wtoi(w) ( (w.hibyte<<8) + w.lobyte) #define itow(dst,src) dst.hibyte = (src & 0xff00) >> 8;\ dst.lobyte = src & 0xff; struct ludir { /* Internal library ldir structure */ unsigned char l_stat; /* status of file */ char l_name[8]; /* name */ char l_ext[3]; /* extension */ word l_off; /* offset in library */ word l_len; /* lengty of file */ char l_fill[16]; /* pad to 32 bytes */ } ldir[MAXFILES]; int errcnt, nfiles, nslots; bool verbose = false; char *cmdname; char *getname(), *sprintf(); int update(), reorg(), table(), extract(), print(), delete(); main (argc, argv) int argc; char **argv; { register char *flagp; char *aname; /* name of library file */ int (*function)() = NULL; /* function to do on library */ /* set the function to be performed, but detect conflicts */ #define setfunc(val) if(function != NULL) conflict(); else function = val cmdname = argv[0]; 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 'p': setfunc(print); break; case 'd': setfunc(delete); break; case 'r': setfunc(reorg); break; case 'v': verbose = true; break; default: help (); } if(function == NULL) { fprintf(stderr,"No function key letter specified\n"); help(); } (*function)(aname); } /* print error message and exit */ help () { fprintf (stderr, "Usage: %s {utepdr}[v] library [files] ...\n", cmdname); fprintf (stderr, "Functions are:\n\tu - Update, add files to library\n"); fprintf (stderr, "\tt - Table of contents\n"); fprintf (stderr, "\te - Extract files from library\n"); fprintf (stderr, "\tp - Print files in library\n"); fprintf (stderr, "\td - Delete files in library\n"); fprintf (stderr, "\tr - Reorginize library\n"); fprintf (stderr, "Flags are:\n\tv - Verbose\n"); exit (1); } conflict() { fprintf(stderr,"Conficting keys\n"); help(); } error (str) char *str; { fprintf (stderr, "%s: %s\n", cmdname, str); exit (1); } cant (name) char *name; { extern int errno; extern char *sys_errlist[]; fprintf (stderr, "%s: %s\n", name, sys_errlist[errno]); exit (1); } /* Get file names, check for dups, and initialize */ filenames (ac, av) char **av; { register int i, j; errcnt = 0; for (i = 0; i < ac - 3; i++) { fname[i] = 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])) { fprintf (stderr, "%s", fname[i]); error (": duplicate file name"); } } table (lib) char *lib; { FILE *lfd; register int i, total; int active = 0, unused = 0, deleted = 0; char *uname; if ((lfd = fopen (lib, "r")) == NULL) cant (lib); getdir (lfd); total = wtoi(ldir[0].l_len); if(verbose) { printf("Name Index Length\n"); printf("Directory %4d\n", 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); if (filarg (uname)) if(verbose) printf ("%-12s %4d %4d\n", uname, wtoi (ldir[i].l_off), wtoi (ldir[i].l_len)); else printf ("%s\n", uname); total += wtoi(ldir[i].l_len); break; case UNUSED: unused++; break; default: deleted++; } if(verbose) { printf("--------------------------\n"); printf("Total sectors %4d\n", total); printf("\nLibrary %s has %d slots, %d deleted %d active, %d unused\n", lib, nslots, deleted, active, unused); } VOID fclose (lfd); not_found (); } getdir (f) FILE *f; { rewind(f); if (fread ((char *) & ldir[0], DSIZE, 1, f) != 1) error ("No directory\n"); nslots = wtoi (ldir[0].l_len) * SLOTS_SEC; if (fread ((char *) & ldir[1], DSIZE, nslots, f) != nslots) error ("Can't read directory - is it a library?"); } putdir (f) FILE *f; { rewind(f); if (fwrite ((char *) ldir, DSIZE, nslots, f) != nslots) error ("Can't write directory - library may be botched"); } initdir (f) FILE *f; { register int i; int numsecs; char line[80]; static struct ludir blankentry = { UNUSED, { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }, { ' ', ' ', ' ' }, }; for (;;) { printf ("Number of slots to allocate: "); if (fgets (line, 80, stdin) == NULL) error ("Eof when reading input"); 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; nslots = numsecs * SLOTS_SEC; for (i = 0; i < nslots; i++) ldir[i] = blankentry; ldir[0].l_stat = ACTIVE; itow (ldir[0].l_len, numsecs); putdir (f); } /* convert nm.ex to a Unix style string */ char *getname (nm, ex) char *nm, *ex; { static char namebuf[14]; register char *cp, *dp; for (cp = namebuf, dp = nm; *dp != ' ' && dp != &nm[8];) *cp++ = isupper (*dp) ? tolower (*dp++) : *dp++; *cp++ = '.'; for (dp = ex; *dp != ' ' && dp != &ex[3];) *cp++ = isupper (*dp) ? tolower (*dp++) : *dp++; *cp = '\0'; return namebuf; } 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 { fprintf (stderr, "%s: name truncated\n", unixname); 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]) { fprintf (stderr, "%s: not in library.\n", fname[i]); errcnt++; } } extract(name) char *name; { getfiles(name, false); } print(name) char *name; { getfiles(name, true); } getfiles (name, pflag) char *name; bool pflag; { FILE *lfd, *ofd; register int i; char *unixname; if ((lfd = fopen (name, "r")) == NULL) cant (name); ofd = pflag ? stdout : NULL; 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; fprintf(stderr,"%s", unixname); if (ofd != stdout) ofd = fopen (unixname, "w"); if (ofd == NULL) { fprintf (stderr, " - can't create"); errcnt++; } else { VOID fseek (lfd, (long) wtoi (ldir[i].l_off) * SECTOR, 0); acopy (lfd, ofd, wtoi (ldir[i].l_len)); if (ofd != stdout) VOID fclose (ofd); } putc('\n', stderr); } VOID fclose (lfd); not_found (); } acopy (fdi, fdo, nsecs) FILE *fdi, *fdo; register unsigned int nsecs; { register int i, c; int textfile = 1; while( nsecs-- != 0) for(i=0; i= nslots) { fprintf (stderr, "%s: can't add library is full\n",name); errcnt++; return; } ldir[i].l_stat = ACTIVE; putname (ldir[i].l_name, name); VOID fseek(lfd, 0L, 2); /* append to end */ secoffs = ftell(lfd) / SECTOR; itow (ldir[i].l_off, secoffs); numsecs = fcopy (ifd, lfd); itow (ldir[i].l_len, numsecs); VOID fclose (ifd); } fcopy (ifd, ofd) FILE *ifd, *ofd; { register int total = 0; register int i, n; char sectorbuf[SECTOR]; while ( (n = fread( sectorbuf, 1, SECTOR, ifd)) != 0) { if (n != SECTOR) for (i = n; i < SECTOR; i++) sectorbuf[i] = CTRLZ; if (fwrite( sectorbuf, 1, SECTOR, ofd ) != SECTOR) error("write error"); ++total; } return total; } delete (lname) char *lname; { FILE *f; register int i; if ((f = fopen (lname, "r+")) == NULL) 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) fprintf (stderr, "errors - library not updated\n"); else putdir (f); VOID fclose (f); } reorg (name) char *name; { FILE *olib, *nlib; int oldsize; register int i, j; struct ludir odir[MAXFILES]; char tmpname[SECTOR]; VOID sprintf(tmpname,"%-10.10s.TMP", name); if( (olib = fopen(name,"r")) == NULL) cant(name); if( (nlib = fopen(tmpname, "w")) == NULL) 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 ) { if(verbose) fprintf(stderr, "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++; fprintf(stderr, "Not enough room in new library\n"); break; } } VOID fclose(olib); putdir(nlib); VOID fclose (nlib); if(errcnt == 0) { if ( unlink(name) < 0 || link(tmpname, name) < 0) { VOID unlink(tmpname); cant(name); } } else fprintf(stderr,"Errors, library not updated\n"); VOID unlink(tmpname); } copyentry( old, of, new, nf ) struct ludir *old, *new; FILE *of, *nf; { register int secoffs, numsecs; char buf[SECTOR]; new->l_stat = ACTIVE; copymem(new->l_name, old->l_name, 8); copymem(new->l_ext, old->l_ext, 3); VOID fseek(of, (long) wtoi(old->l_off)*SECTOR, 0); VOID fseek(nf, 0L, 2); secoffs = ftell(nf) / SECTOR; itow (new->l_off, secoffs); numsecs = wtoi(old->l_len); itow (new->l_len, numsecs); while(numsecs-- != 0) { if( fread( buf, 1, SECTOR, of) != SECTOR) error("read error"); if( fwrite( buf, 1, SECTOR, nf) != SECTOR) error("write error"); } } copymem(dst, src, n) register char *dst, *src; register unsigned int n; { while(n-- != 0) *dst++ = *src++; }