/* * f l x . c * * Read and write Dec "Dos" magtapes on Unix. The tape is mounted * on device 0. * * Usage * flx option files * * Options * * a Ascii -- extract removes '\r' and '\0' from the file. * c Create -- write files to the tape. * t Table -- print a tape directory. * v Verbose -- log files as seen. * u [ppn] Uic -- the next argument is a project-programmer * number. The values are in octal unless * they are followed by '.': * [100,2] == [64.,2.] * * Wild -- Extract all files from all Uic's */ #include long tbuf; #include #define NBLOCK 512 #define MINREC 14 struct label { int l_name0[2]; /* Two radix 50 name words */ int l_type[1]; /* Radix 50 type */ char l_prog; /* Programmer number */ char l_proj; /* Project number */ int l_prot; /* Protection code */ int l_date; /* Date (in some format) */ int l_name1[1]; /* One radix 50 name word */ }; int func; int aflag; int vflag; int starflag; int estat; char bbuf[NBLOCK]; char *bbufpt; int mtfd; char rmt[] = "/dev/rmt0"; /* Device to read from */ char rmtn[] = "/dev/rmtn"; /* This needs editing */ int proj = 1; int prog = 1; main(argc, argv) char *argv[]; { register char *p; register c, i; if (argc < 2) usage(); i = 2; p = argv[1]; while ((c = *p++) != 0) { switch (c) { case 'c': /* Create magtape */ case 't': /* Table (directory) */ case 'x': /* Extract from tape */ if (func != 0) usage(); func = c; break; case 'u': /* Set ppn */ if (i >= argc) usage(); projprog(argv[i++]); break; case 'v': /* Verbose */ ++vflag; break; case 'a': /* Ascii -- drop */ ++aflag; break; case '*': /* Force *.* */ ++starflag; break; default: usage(); } } switch (func) { case 'c': create(i, argc, argv); break; case 't': table(); break; case 'x': extract(i, argc, argv); break; default: usage(); } exit (estat); } /* * The argument `cp' is a pointer * to a string of the form `[nnn,nnn]'; * i.e. a project-programmer number. * Reset the external variables `proj' * and `prog' to agree with the arg. * Quit if the format is bad. */ projprog(cp) register char *cp; { register c; register int ippn; int decimal; if (*cp++ != '[') badppn(); proj = 0; decimal = 0; ippn = atoi(cp); while ((c = *cp++)>='0' && c<='9') { if (c == '8' || c == '9') decimal = 1; proj = 8*proj + c - '0'; } if (c == '.') { c = *cp++; decimal = 1; } if (decimal) proj = ippn; if (c != ',') badppn(); prog = 0; decimal = 0; ippn = atoi(cp); while ((c = *cp++)>='0' && c<='9') { if (c == '8' || c == '9') decimal = 1; prog = 8*prog + c - '0'; } if (c == '.') { c = *cp++; decimal = 1; } if (decimal) prog = ippn; if (c != ']') badppn(); if (vflag) { fprintf(stderr, "[%03o,%03o] == (%d.,%d.)\n", proj, prog, proj, prog); } } /* * Print out a diagnostic for * bad project-programmer numbers and * quit. */ badppn() { diag(1, "bad [project,programmer] specification"); } /* * Process the table command. * Read through the FLX tape, printing out * the information in the headers. The data * blocks are counted as they are read, just * in case this is `tv'. Quit on the double * tapemark at the end. */ table() { register fd, n, nblocks; struct label lb; if ((fd=open(rmt, 0)) < 0) diag(1, "cannot open tape"); for (;;) { if ((n=readlabel(fd, (char *)&lb, sizeof(lb))) <= 0) break; nblocks = 0; while (read(fd, bbuf, sizeof(bbuf)) > 0) ++nblocks; n = printname(&lb); if (vflag) { while (n < 14) { putchar(' '); ++n; } printf("%5d", nblocks); printf(" [%03o,%03o]", lb.l_proj&0377, lb.l_prog&0377); printdate(lb.l_date); printf(" <%03o>", lb.l_prot&0377); } putchar('\n'); } close(fd); if (n != 0) diag(1, "tape read error"); } /* * Given a pointer to a structure * containing the FLX file label record, * convert the name from RADIX 50 to * Ascii and write it to the standard output. * The number of characters written out is * returned, just in case this is `tv'. */ printname(lbp) struct label *lbp; { register char *p; register c, n; char b[9]; r50toa(&b[0], lbp->l_name0, 2); r50toa(&b[6], lbp->l_name1, 1); n = 0; p = &b[0]; while (p<&b[9] && (c = *p++)!=' ') { putchar(c); ++n; } if (lbp->l_type[0] != 0) { putchar('.'); ++n; r50toa(&b[0], lbp->l_type, 1); p = &b[0]; while (p<&b[3] && (c = *p++)!=' ') { putchar(c); ++n; } } return (n); } /* * Convert `nr50' words worth of * RADIX 50 data, pointed to by the argument * `r50p', into `3*nr50' bytes of Ascii and * store the characters into the buffer * pointed to by the `cp' argument. The output * string is in lower case. The illegal code * in RADIX 50 is converted to a `?'. */ static char ctable[] = { " abcdefghijklmnopqrstuvwxyz$.?0123456789" }; r50toa(cp, r50p, nr50) register char *cp; int *r50p; { register unsigned r50; register acx; while (nr50--) { r50 = *r50p++; acx = 0; while (r50 >= 03100) { r50 -= 03100; ++acx; } *cp++ = ctable[acx]; acx = 0; while (r50 >= 050) { r50 -= 050; ++acx; } *cp++ = ctable[acx]; *cp++ = ctable[r50]; } } /* * Take a date word, convert the * date to a human being format and print * it on the standard output. The date word * is encoded as `1000*(year-1970)+daynum', * where January 1 is day number 1. */ static char monlen[] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static char *monname[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; printdate(date) { register month, day, year; year = (date/1000) + 1970; day = date%1000; monlen[1] = ((year%4)==0 && (year%100)!=0) ? 29 : 28; month = 0; while (day > 31) day -= monlen[month++]; printf(" %02d-%s-%02d", day, monname[month], year-1900); } extract(i, argc, argv) char *argv[]; { register char *p; register c, n; register fd; struct label lb; register FILE *ofp; char ofn[40]; if ((fd=open(rmt, 0)) < 0) diag(1, "cannot open tape drive"); while ((n = readlabel(fd, &lb, sizeof (lb))) > 0) { ofp = NULL; makename(ofn, &lb); if (starflag || named(ofn, i, argc, argv)) { if (vflag) printf("Extract: %s\n", ofn); stripuic(ofn); if ((ofp=fopen(ofn, "w")) == NULL) diag(0, "%s: cannot create output", ofn); } else if (vflag) printf("Skipped: %s\n", ofn); while ((n=read(fd, bbuf, sizeof(bbuf))) > 0) { if (ofp != NULL) { p = &bbuf[0]; while (n--) { c = (*p++) & 0377; if (aflag == 0 || (c != 0 && c != 015)) putc(c, ofp); } } } if (ofp != NULL) fclose(ofp); if (n < 0) printf("Read error, code %d\n", n); /* if (n != 0) break; */ } close(fd); if (n != 0) diag(1, "tape error"); } int readlabel(fd, buffer, buffersize) FILE *fd; char *buffer; int buffersize; /* * Read a tape label. Return 0 at eof (standard Unix) or -1 (Venix) */ { register int n; n = read(fd, buffer, buffersize); if (n <= 0) { printf("readlabel, at eof\n", n); } return (n); } named(ofn, i, argc, argv) char *ofn; char *argv[]; { if (i == argc) return (1); while (i < argc) { if (strcmp(ofn, argv[i]) == 0) return (1); ++i; } return (0); } makename(cp, lp) register char *cp; struct label *lp; { register char *bp; register c; char b[9]; sprintf(cp, "[%03o,%03o]", lp->l_proj&0377, lp->l_prog&0377); while (*cp != 0) ++cp; r50toa(&b[0], lp->l_name0, 2); r50toa(&b[6], lp->l_name1, 1); bp = &b[0]; while (bp<&b[9] && (c = *bp++)!=' ') *cp++ = c; if (lp->l_type[0] != 0) { *cp++ = '.'; r50toa(&b[0], lp->l_type, 1); bp = &b[0]; while (bp<&b[3] && (c = *bp++)!=' ') *cp++ = c; } *cp = 0; } stripuic(cp1) register char *cp1; { register char *cp2; cp2 = cp1; while (*cp2 != 0) ++cp2; while (cp2!=cp1 && cp2[-1]!=']') --cp2; if (cp2 != cp1) { while (*cp1++ = *cp2++) ; } } create(i, argc, argv) char *argv[]; { register FILE *ifp; register c; register nbytes; struct label lb; if (i == argc) diag(1, "no files"); while (i < argc) { if ((ifp=fopen(argv[i], "r")) == NULL) diag(0, "%s: cannot open", argv[i]); else { if (vflag) printf("Creating: %s\n", argv[i]); if ((mtfd=open(rmtn, 1)) < 0) diag(1, "cannot open tape drive"); makelabel(&lb, argv[i]); if (write(mtfd, (char *)&lb, sizeof(lb)) != sizeof(lb)) diag(1, "tape write error"); nbytes = 0; bbufpt = &bbuf[0]; while ((c=getc(ifp)) != EOF) { if (aflag!=0 && c=='\n') { byte('\r'); ++nbytes; } byte(c); ++nbytes; } if ((nbytes&01) != 0) byte(0); flushtape(); close(mtfd); fclose(ifp); } ++i; } } byte(b) register b; { if (bbufpt >= &bbuf[NBLOCK]) { flushtape(); bbufpt = &bbuf[0]; } *bbufpt++ = b; } flushtape() { register n; if ((n=bbufpt-&bbuf[0]) != 0) { /* * No record can be shorter than * MINREC (14) bytes. So says ANSI and * so checks the TJE16 tape drive. */ while (n < MINREC) { *bbufpt++ = 0; ++n; } if (write(mtfd, bbuf, n) != n) diag(1, "tape write error"); } } makelabel(lp, ifn) struct label *lp; char *ifn; { register char *cp2, *cp3; register c; struct tm *tmp; struct tm *localtime(); char b[12]; cp2 = ifn; while (cp2[0] != 0) ++cp2; while (cp2!=ifn && cp2[-1]!='/') --cp2; cp3 = &b[0]; while ((c = *cp2++)!=0 && c!='.') { if (cp3 < &b[9]) *cp3++ = c; } while (cp3 < &b[9]) *cp3++ = ' '; if (c == '.') { while ((c = *cp2++) != 0) { if (cp3 < &b[12]) *cp3++ = c; } } while (cp3 < &b[12]) *cp3++ = ' '; ator50(lp->l_name0, 2, &b[0]); ator50(lp->l_name1, 1, &b[6]); ator50(lp->l_type, 1, &b[9]); time(&tbuf); tmp = localtime(&tbuf); lp->l_date = 1000*(tmp->tm_year-70) + tmp->tm_yday + 1; lp->l_proj = proj; lp->l_prog = prog; lp->l_prot = 0233; } ator50(r50p, nr50, cp) register int *r50p; register char *cp; { register r50; while (nr50--) { r50 = 050*050*r50char(*cp++); r50 += 050*r50char(*cp++); r50 += r50char(*cp++); *r50p++ = r50; } } r50char(c) register c; { if (c == ' ') return (0); if (c>='A' && c<='Z') return (c - 'A' + 01); if (c>='a' && c<='z') return (c - 'a' + 01); if (c == '$') return (033); if (c == '.') return (034); if (c>='0' && c<='9') return (c - '0' + 036); return (035); } diag(flag, a) { fprintf(stderr, "flx: %r\n", &a); if (flag != 0) exit(1); estat = 1; } usage() { fprintf(stderr, "Usage: flx [actvx*] [u uic] [files ...]\n"); fprintf(stderr, " a\tAscii extract -- ignore and \n"); fprintf(stderr, " c\tCreate -- write files to magtape\n"); fprintf(stderr, " t\tTable -- print tape directory\n"); fprintf(stderr, " v\tVerbose -- log files as seen\n"); fprintf(stderr, " *\tCopy all files (*.*) from all accounts\n"); fprintf(stderr, " u\tUic. Next arg is [proj,prog]\n"); fprintf(stderr, "\t\tThe '[', ',', and ']' are mandatory.\n"); fprintf(stderr, "\t\tDecimal uic's are allowed, use '.'\n"); exit(1); }