home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume7 / cmstape / cmstape.c < prev    next >
Encoding:
C/C++ Source or Header  |  1986-11-30  |  35.4 KB  |  1,235 lines

  1. /* $Header: cmstape.c,v 1.1 86/07/18 10:44:17 alan Rel $
  2.  *
  3.  * NAME
  4.  *    CMSTAPE - manipulate a CMS TAPE DUMP tape.
  5.  *
  6.  * SYNOPSIS
  7.  *    cmstape [-tvcrxfiud] tape [-Fnn] file...
  8.  *
  9.  * DESCRIPTION
  10.  *
  11.  *    Cmstape manipulates an IBM CMS tape.  Flags are:
  12.  *     -t (default) type tape directory of tape file.
  13.  *     -v verbose
  14.  *     -c create a CMS tape dump
  15.  *     -r append to a CMS tape dump ('r' is non-mnemonic but matches tar)
  16.  *     -x extract files from tape.  If no names given all are extracted.
  17.  *        otherwise only those named are extracted.  Syntax for names is: fn.ft
  18.  *     -f tape is in readtape(1) format: each file block is preceded by
  19.  *        a two byte block length.
  20.  *     -i image (no translation or tacking on of newlines).  For V-format files
  21.  *        the extracted/written file includes a halfword length preceding each
  22.  *        record.  This makes image extracts and creates a reversible process
  23.  *        for V as well as F format files.
  24.  *     -u don't convert filenames to lowercase and vice-versa
  25.  *     -d debug (multiple occurences increase volume of output)
  26.  *
  27.  *    Reads either from a tape device or from files that have been
  28.  *    read off a tape by readtape(l).  That is,  files that contain
  29.  *    a 2 byte tape block size before each data block (with vax
  30.  *    swapped byte ordering!).
  31.  *
  32.  *    When writing a tape dump,  file names preceded by "-Fnn" cause those
  33.  *    files to be written in fixed instead of the default variable format.
  34.  *    If "nn" is specified,  it will be used for the record length, otherwise
  35.  *    80 is assumed.
  36.  *
  37.  * BUGS
  38.  *    Due to the odd format of CMS tape files,  you will need twice the working
  39.  *    space as that taken up by the final files.  Also,  even files you don't
  40.  *    want to extract get copied to a temporary file and are then discarded
  41.  *    since the name of the file is at the end of the data!
  42.  *
  43.  * FILES
  44.  *    cms??????          temporary work file.
  45.  *    CMSTAPE.CMSUT1     temporary work file when CMS #defined.
  46.  *
  47.  * SEE ALSO
  48.  *    IBM VM/SP Data Areas and Control Block Logic, LY20-0891
  49.  *
  50.  *    Readtape, writetape utilities:  Jim Guyton, Rand Corp.
  51.  *
  52.  * AUTHOR
  53.  *    E. Alan Crosswell
  54.  *    Columbia University
  55.  *    alan@columbia.edu
  56.  *
  57.  * #define CMS when compiling for Waterloo C under CMS.
  58.  *
  59.  * COMPILATION INSTRUCTIONS
  60.  *  4.x BSD:
  61.  *    cc -o cmstape cmstape.c ebcdic.c
  62.  *  Waterloo C/370:
  63.  *    cw cmstape (prm CMS 1
  64.  *    linkc cmstape
  65.  *
  66.  * You may wonder why I wrote this.  It's simple:  I get a lot of CMS tapes
  67.  * in the mail.  The IBM machine room is across campus but the vax is just
  68.  * down the hall and they're both on the network.  Here's how I do it:
  69.  *    1) mount the tape on the vax and use readtape to read all the files
  70.  *       into a directory.
  71.  *    2) login on the IBM and FTP the files from the vax in image mode:
  72.  *       ftp thevax
  73.  *       login me
  74.  *       represent i
  75.  *       get file1 file1.file
  76.  *       get file2 file2.file
  77.  *         etc.
  78.  *    3) crank up cmstape on the IBM:
  79.  *       cmstape -xvfi file1
  80.  *       cmstape -xvfi file2
  81.  *         etc.
  82.  *
  83.  * $Log:    cmstape.c,v $
  84.  * Revision 1.1  86/07/18  10:44:17  alan
  85.  * Initial revision
  86.  * 
  87.  */
  88.  
  89.  
  90. /* various header files */
  91.  
  92. #ifdef IBM370                   /* future version of Wloo C may predefine it */
  93. # define CMS 1
  94. #endif IBM370
  95.  
  96. #include <stdio.h>
  97. #include <ctype.h>
  98. #ifdef CMS
  99. # include <time.h>
  100. # include <file.h>
  101. # include <stdlib.h>
  102. # include <string.h>
  103. # include <types.h>
  104. # include <stat.h>
  105. #else
  106. # include <strings.h>
  107. # include <sys/time.h>
  108. # include <sys/types.h>
  109. # include <sys/file.h>
  110. # include <sys/stat.h>
  111. #endif CMS
  112.  
  113. /*
  114.  * Format of a CMS TAPE DUMP tape entry for each file:
  115.  *
  116.  * 1) One or more blocks like this:
  117.  *      +--+-+-+-+-+-+-+-----/ /-+-+-+-----
  118.  *      |02|C M S V| l | data... | l | data...
  119.  *      +--+-+-+-+-+-+-+-----/ /-+-+-+-----
  120.  *       <-----------4101 bytes max---------->
  121.  *
  122.  *    or this (for fixed records (lrecl in the FST)):
  123.  *      +--+-+-+-+-+-----/ /-+-+-+-----
  124.  *      |02|C M S F| data... | l | data...
  125.  *      +--+-+-+-+-+-----/ /-+-+-+-----
  126.  *       <-----------4101 bytes max---------->
  127.  *
  128.  * 2) Followed by an ending FST + filename
  129.  *      +--+-+-+-+-+------------+
  130.  *      |02|C M S N|  FST data  |
  131.  *      +--+-+-+-+-+------------+
  132.  *       <-------87 bytes------>
  133.  *
  134.  * There are a whole bunch of other CMSx letters as well but I don't know
  135.  * what they mean and have never seen one on a tape I've received.  Some
  136.  * have to do with sparse files.
  137.  *
  138.  * Many CMS files may be in a single physical tape file.  Notice that since
  139.  * the FST comes after the data,  there is no way to know the record length
  140.  * of "F" (fixed) files until after the entire file has been read.
  141.  * "V" (variable) files have the length of each record in front of
  142.  * the record.
  143.  *
  144.  * The tape fst written into the CMSN block is actually the CMS fst starting
  145.  * a few words from the beginning,  then some filler,  then the filename.
  146.  */
  147.  
  148. #ifdef CMS
  149. typedef unsigned char u_char;
  150. #endif CMS
  151. typedef struct {
  152.     u_char f_wp[2];             /* write pointer - IBM byte ordering */
  153.     u_char f_rp[2];             /* read pointer */
  154.     u_char f_mode[2];           /* mode */
  155.     u_char f_itcnt[2];          /* item count */
  156.     u_char f_fcl[2];            /* first chain link */
  157.     u_char f_recfm;             /* record format: F or V */
  158.     u_char f_flag;              /* flag */
  159.     u_char f_lrecl[4];          /* record length */
  160.     u_char f_blkcnt[2];         /* data block count */
  161.     u_char f_date[6];           /* date FYFYMMDDHHNN */
  162.     u_char f_fop[4];            /* file origin pointer */
  163. /* beginning of alternate counts (added with EDF file system) */
  164.     u_char f_ablkcnt[4];        /* alternate block count */
  165.     u_char f_areccnt[4];        /* alternate record count */
  166.     u_char f_nlvl;              /* number of ptr block levels */
  167.     u_char f_ptrs;              /* fst pointer size */
  168.     u_char f_adate[6];          /* alternate date YYMMDDHHNNSS */
  169.     u_char f_filler[20];        /* fill out to 64 bytes */
  170. /* filename tacked on after fst */
  171.     u_char f_fn[8];
  172.     u_char f_ft[8];
  173.     u_char f_fm[2];
  174. } tapefst;                      /* overall length 82 bytes */
  175.  
  176. /* what's at the beginning of each tape block */
  177. typedef struct {
  178.     u_char cms[4];              /* 02 CMS */
  179.     u_char fmt;                 /* F | V | N | ... */
  180. } tapehead;
  181.  
  182. #define F 0xc6                  /* fixed */
  183. #define V 0xe5                  /* variable */
  184. #define N 0xd5                  /* end of file */
  185.  
  186.  
  187. /* macros for ebcdic to local character set and byte ordering */
  188. #ifdef CMS
  189. # define ETOL(c) (c)
  190. # define LTOE(c) (c)
  191. #else
  192. extern char etoa[], atoe[];
  193. # define ETOL(c) (etoa[(c)&0xff])
  194. # define LTOE(c) (atoe[(c)&0xff])
  195. #endif CMS
  196. #define IBMTOINT(x) (((x[0]&0xff)<<24) + ((x[1]&0xff)<<16) \
  197.                      + ((x[2]&0xff)<<8) + (x[3]&0xff))
  198. #define IBMTOSHORT(x) (((x[0]&0xff)<<8) + (x[1]&0xff))
  199. #define INTTOIBM(a,b) a[0]=(b>>24)&0xff, a[1]=(b>>16)&0xff, \
  200.                      a[2]=(b>>8)&0xff, a[3]=(b&0xff)
  201. #define SHORTTOIBM(a,b) a[0]=(b>>8)&0xff, a[1]=(b&0xff)
  202.  
  203. /* hi and lo nibbles of a byte (packed decimal) */
  204. #define HI(x) ((x)>>4)
  205. #define LO(x) ((x)&0x0f)
  206. #define PACK(x) (((((x)/10)&0xf) << 4) | (((x)%10)&0xf))
  207.  
  208. /* Globals */
  209.  
  210.  
  211. #define MAXBUF 1<<16            /* max size file record */
  212. #define DEFTAPE "/dev/nrmt8"    /* default tape name */
  213. #define TAPEDATA 4096           /* size of data part of tape block */
  214.  
  215. static int Fflag = 0;           /* indicates readtape(l) file */
  216. static int Tflag = 0;           /* just give a directory */
  217. static int Vflag = 0;           /* verbose */
  218. static int Xflag = 0;           /* read files off tape */
  219. static int Cflag = 0;           /* write files onto tape */
  220. static int Rflag = 0;           /* append files onto tape */
  221. static int Iflag = 0;           /* image mode (instead of text) */
  222. static int Uflag = 0;           /* uppercase converted file names */
  223. static int Debug = 0;           /* debug */
  224. static char *Tapename = NULL;   /* tape file name */
  225. static char **Fname = NULL;     /* ptr to array of file names */
  226. static int Fcount = 0;          /* number in above array */
  227. static char *Cmdname;           /* name invoked as */
  228. static char *Bufp = NULL;       /* buffer pointer */
  229. static int Lrecl = 0;           /* record length */
  230. #ifdef CMS
  231. static struct stat St;          /* used for CMS tape dumps */
  232. static char Tempname[] = "cmstape.cmsut1 (bin lrecl 65535";
  233. #else
  234. static char Template[] = "cmsXXXXXX";
  235. static char Tempname[sizeof(Template)];
  236. #endif CMS
  237. static FILE *Tempf = NULL;      /* shared by dodata and doeof */
  238. struct nametab {                /* ebcdic name table */
  239.     char name[16];
  240. } *Nametab = NULL;
  241.  
  242. static char CMSh[] = {0x02, 0xc3, 0xd4, 0xe2 };
  243.                     /*  02    C     M     S  */
  244.  
  245. static char *Month[12] = {
  246.     "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
  247.     "Sep", "Oct", "Nov", "Dec" };
  248.  
  249. /* shared by dumprec and dumpeof */
  250. static char Tapebuf[TAPEDATA+sizeof(tapehead)];
  251. static char *Tbp;               /* ptr into tapebuf */
  252. static int Tbrem;               /* remainder length */
  253. static int Recs;                /* records dumped */
  254. static int Blocks;              /* blocks dumped */
  255. static int Maxrecl;             /* max lrecl */
  256.  
  257.  
  258. /*
  259.  * the main program.  All the work is done in readtape() and writetape()
  260.  * (not to be confused with tape utilities of the same name).
  261.  */
  262. main(argc, argv)
  263. {
  264.     options(argc, argv);        /* parse options */
  265.     initialize();               /* intialize junk */
  266.     if (Cflag)
  267.       writetape();              /* output */
  268.     else
  269.       readtape();               /* input */
  270.     exit(0);
  271. }
  272.  
  273. static
  274. options(argc, argv)             /* parse options */
  275. int argc;
  276. char **argv;
  277. {
  278.     Cmdname = argv[0];
  279.     for(--argc, ++argv; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
  280.         char *cp = argv[0];
  281.         while (*++cp) switch(*cp) {
  282.           case 'f':
  283.             Fflag++;
  284.             continue;
  285.           case 't':
  286.             Tflag++;
  287.             continue;
  288.           case 'v':
  289.             Vflag++;
  290.             Tflag++;
  291.             continue;
  292.           case 'x':
  293.             Xflag++;
  294.             continue;
  295.           case 'c':
  296.             Cflag++;
  297.             continue;
  298.           case 'i':
  299.             Iflag++;
  300.             continue;
  301.           case 'd':
  302.             Debug++;
  303.             continue;
  304.           case 'u':
  305.             Uflag++;
  306.             continue;
  307.           case 'r':
  308.             Cflag++;
  309.             Rflag++;
  310.             continue;
  311.           default:
  312.             usage();
  313.         }
  314.     }
  315.     Tapename = (argc--)? *argv++ : DEFTAPE;
  316.     if (argc > 0) {
  317.         Fname = argv;
  318.         Fcount = argc;
  319.     }
  320.     if (Xflag+Cflag == 0)
  321.       Tflag = 1;
  322.     else if (Xflag+Cflag > 1) {
  323.         fprintf(stderr,"%s: Can't extract and create at the same time!\n",
  324.                 Cmdname);
  325.         exit(1);
  326.     }
  327.     if (Debug)
  328.       printoptions();
  329. }
  330.  
  331. /*
  332.  * get a buffer for I/O and build table of file names
  333.  */
  334. static
  335. initialize()
  336. {
  337.     char *malloc(), *unix2cms();
  338.  
  339. #ifdef CMS
  340.     setiomsg(1);
  341. #endif CMS
  342.     if ((Bufp = malloc(MAXBUF+2)) == NULL) /* get a buffer */
  343.       croak("malloc");
  344.     if (Fcount && (Cflag || Xflag)) { /* user gave list of names */
  345.         int i;
  346.  
  347.         if ((Nametab = (struct nametab *)malloc(Fcount * sizeof(Nametab[0])))
  348.             == NULL)
  349.           croak("malloc");
  350.         if (Debug > 1)
  351.           fprintf(stderr,"Name table:\n");
  352.         for (i = 0; i < Fcount; i++) {
  353.             bcopy(unix2cms(Fname[i]),Nametab[i].name,sizeof(Nametab[0].name));
  354.             if (Debug > 1) {
  355.                 int j;
  356.                 for (j = 0; j < sizeof(Nametab[0].name); j++)
  357.                   fputc(ETOL(Nametab[i].name[j]), stderr);
  358.                 fputc('\n', stderr);
  359.             }
  360.         }
  361.     }
  362. }
  363.  
  364. static
  365. printoptions()                  /* print out options */
  366. {
  367.     int i;
  368.  
  369.     fprintf(stderr,"%s: switches: ", Cmdname);
  370.     if (Fflag) fputc('f',stderr);
  371.     if (Tflag) fputc('t',stderr);
  372.     if (Xflag) fputc('x',stderr);
  373.     if (Cflag) fputc('c',stderr);
  374.     if (Iflag) fputc('i',stderr);
  375.     if (Uflag) fputc('u',stderr);
  376.     if (Vflag) fputc('v',stderr);
  377.     if (Rflag) fputc('r',stderr);
  378.     if (Debug) fputc('d',stderr);
  379.     fprintf(stderr,"\n\ttape '%s'\n", Tapename);
  380.     if (Fcount) {
  381.         fprintf(stderr,"\t%d files:", Fcount);
  382.         for (i = 0; i < Fcount; i++)
  383.           fprintf(stderr, " %s", Fname[i]);
  384.         fprintf(stderr,".\n");
  385.     }
  386. }
  387.  
  388. static
  389. usage()
  390. {
  391.     fprintf(stderr,"%s: Usage: %s [-ftvxcdiu] tape name...\n", Cmdname,
  392.             Cmdname);
  393.     exit(1);
  394. }
  395.  
  396. static
  397. readtape()                      /* read a cms format tape */
  398. {
  399.     int tapefd;
  400.     int len;
  401.     tapehead *t = (tapehead *)Bufp;
  402.     char *dp = Bufp + sizeof(*t);
  403. #undef NAME
  404. #ifdef CMS
  405. # define NAME wlooname
  406.     char wlooname[100];
  407.     strcpy(wlooname,Tapename);
  408.     strcat(wlooname,"(bin");
  409. #else
  410. # define NAME Tapename
  411. #endif CMS
  412.     if ((tapefd = open(NAME, O_RDONLY)) < 0)
  413.       croak(Tapename);
  414.  
  415.     while ((len = readblock(tapefd)) > 0) {
  416.         if (Debug > 1)
  417.           fprintf(stderr,"readblock return len=%d\n", len);
  418.         if (len < sizeof(tapehead) || bcmp(t->cms,CMSh,sizeof(CMSh)))
  419.           die("%s: not a CMS tape\n", Tapename);
  420.         len -= sizeof(*t);      /* subtract length of header */
  421.         switch (t->fmt) {
  422.           case N:               /* EOF */
  423.             doeof(dp,len);
  424.             break;
  425.           case F:               /* F data */
  426.           case V:               /* V data */
  427.             if (Xflag)
  428.               dodata(dp,len);   /* copy the data and fix it up later */
  429.             break;
  430.           default:
  431.             die("%s: CMS format %c not supported.\n",
  432.                     Tapename, ETOL(t->fmt));
  433.         }
  434.     }
  435.  
  436. }
  437.  
  438. /*
  439.  * copy the data verbatim to the temp file.
  440.  */
  441. dodata(b,l)
  442. char *b;
  443. int l;
  444. {
  445.     if (Debug > 1) {
  446.         fprintf(stderr,"data block, len=%d\n", l);
  447.         if (Debug > 2)
  448.           dump(b-sizeof(tapehead),l+sizeof(tapehead));
  449.     }
  450.     if (Tempf == NULL) {        /* first time thru */
  451. #ifndef CMS
  452.         strcpy(Tempname,Template);
  453.         mktemp(Tempname);
  454. #endif
  455.         Tempf = fopen(Tempname,"w+");
  456.         if (Tempf == NULL)
  457.           croak(Tempname);
  458.     }
  459.     if (fwrite(b, l, 1, Tempf) == NULL)
  460.       croak(Tempname);
  461. }
  462.  
  463. char *cms2unix();
  464.  
  465. /*
  466.  * process the EOF block.  Depending on options selected:
  467.  *   - copy the temp file using the recfm and lrecl with possible translations.
  468.  *   - type out the fst info in a manner similar to ls.
  469.  */
  470. static
  471. doeof(b,l)
  472. tapefst *b;
  473. int l;
  474. {
  475.     if (Debug) {
  476.         fprintf(stderr,"EOF block, len=%d\n", l);
  477.         prtfst(b);
  478.     }
  479.     if (l < sizeof(tapefst))
  480.       die("%s: file label too short.\n", Tapename);
  481.     Lrecl = IBMTOINT(b->f_lrecl);
  482.     if (in_nametab(b)) {
  483.         if (Tflag)
  484.           prinfo(b);
  485.         if (Xflag) {            /* actually reading the data */
  486.             if (Tempf == NULL) {
  487.                 fprintf(stderr,"%s: Empty file.  Skipping...\n",Tapename);
  488.                 return;
  489.             }
  490. #ifndef CMS
  491.             if (Iflag) {        /* when not CMS, this is simply a rename */
  492.                 fclose(Tempf);
  493.                 Tempf = NULL;
  494.                 if (rename(Tempname, cms2unix(b)) < 0)
  495.                   perror(Tempname);
  496.                 return;
  497.             }
  498. #endif CMS
  499.             /* call V_ or F_close for cooked files always and image files
  500.                when under CMS.  Image files under unix were handled above. */
  501.             if (b->f_recfm == V)
  502.               V_close(b);
  503.             else
  504.               F_close(b);
  505.         }
  506.     } /* end if in Nametab[] */
  507.     if (Xflag && Tempf != NULL) { /* clean up when not in Nametab[] */
  508.         fclose(Tempf);
  509.         Tempf = NULL;
  510.         if (!Debug)
  511.           unlink(Tempname);
  512.         else
  513.           fprintf(stderr,"%s: not unlinked (debug)\n", Tempname);
  514.     }
  515. }
  516.  
  517. static
  518. prinfo(b)
  519. tapefst *b;
  520. {
  521.     int lrecl, reccnt, year, mon, day, hour, min;
  522.     int i;
  523.     struct tm *tp;
  524.     long tic;
  525.  
  526.     if (Vflag) {
  527.         lrecl = IBMTOINT(b->f_lrecl);
  528.         reccnt = IBMTOINT(b->f_areccnt);
  529.         year = HI(b->f_adate[0]) * 10 + LO(b->f_adate[0]);
  530.         mon =  HI(b->f_adate[1]) * 10 + LO(b->f_adate[1]);
  531.         day =  HI(b->f_adate[2]) * 10 + LO(b->f_adate[2]);
  532.         hour = HI(b->f_adate[3]) * 10 + LO(b->f_adate[3]);
  533.         min =  HI(b->f_adate[4]) * 10 + LO(b->f_adate[4]);
  534. #ifdef CMS
  535.         tp = localtime();       /* waterloo goofed! */
  536. #else
  537.         time(&tic);
  538.         tp = localtime(&tic);
  539. #endif CMS
  540.         printf("%c/%-4d %8d %s %2d ",ETOL(b->f_recfm),
  541.                lrecl, reccnt, Month[mon-1], day);
  542.         if (tp->tm_year != year)
  543.           printf(" %4d", year);
  544.         else
  545.           printf("%2d:%02d ",hour,min);
  546.     }
  547.     for (i = 0; i < 8; i++)
  548.       putchar(ETOL(b->f_fn[i]));
  549.     putchar(' ');
  550.     for (i = 0; i < 8; i++)
  551.       putchar(ETOL(b->f_ft[i]));
  552.     putchar(' ');
  553.     putchar(ETOL(b->f_fm[0]));
  554.     putchar(ETOL(b->f_fm[1]));
  555.     putchar('\n');
  556. }
  557.  
  558.  
  559. /* V format close.  Rewind the temp file and write the final file */
  560. static
  561. V_close(fst)
  562. tapefst *fst;
  563. {
  564.     int newfd = FVcreat('v',fst);
  565.     int len;
  566.  
  567.     rewind(Tempf);
  568.     Recs = 0;
  569.     while (fread(Bufp, 2, 1, Tempf)) { /* get length header */
  570.         len = IBMTOSHORT(Bufp);
  571.         if (fread(Bufp, len, 1, Tempf) == 0) /* get data */
  572.           croak(Tempname);
  573.         writerec(newfd, Bufp, len); /* write out record */
  574.     }
  575.     close(newfd);
  576. }
  577.  
  578. /* F format close.  Close the temp file, f and rewrite the final
  579.    F format file by calling writerec. */
  580. static
  581. F_close(fst)
  582. tapefst *fst;
  583. {
  584.     int newfd = FVcreat('f',fst);
  585.  
  586.     rewind(Tempf);
  587.     Recs = 0;
  588.     while (fread(Bufp, Lrecl, 1, Tempf)) { /* get record */
  589.         writerec(newfd, Bufp, Lrecl); /* and write it */
  590.     }
  591.     close(newfd);
  592. }
  593.  
  594. static int
  595. FVcreat(recfm,fst)              /* creat final output file */
  596. char recfm;
  597. tapefst *fst;
  598. {
  599.     int newfd;
  600.     char *newname = cms2unix(fst);
  601. #undef NAME
  602. #ifdef CMS
  603. # define NAME wlooname
  604.     char wlooname[100];
  605.  
  606.     sprintf(wlooname,"%s (%s recfm %c lrecl %d", newname,
  607.             (Iflag)?"raw bin":"", recfm, Lrecl);
  608. #else
  609. # define NAME newname
  610. #endif CMS
  611.  
  612.     if ((newfd = creat(NAME,0644)) < 0)
  613.       croak(NAME);
  614.     return newfd;
  615. }
  616.  
  617. /*
  618.  * write record out. Check flags and do ebcdic->ascii translation
  619.  * and adding newline, etc.  When the file is opened "raw" under CMS,
  620.  * each write() call is guaranteed to write a CMS record.
  621.  */
  622. static
  623. writerec(fd,b,l)                /* write a record out to file fd */
  624. int fd;
  625. char *b;
  626. int l;
  627. {
  628.     register int i;
  629.  
  630.     ++Recs;
  631.     if (!Iflag) {               /* text copy */
  632.         if (l == 1 && *b == LTOE(' ')) /* null line */
  633.           l = 0;
  634.         for (i = 0; i < l; i++) /* translate it */
  635.           b[i] = ETOL(b[i]);
  636.         b[l++] = '\n';          /* and add newline */
  637.     }
  638.     if (Debug > 1) {
  639.        fprintf(stderr,"writerec: record %5d length %d:", Recs, l);
  640.        dump(b,l);
  641.     }
  642.     if (write(fd,b,l) != l)
  643.       croak("writerec");
  644. }
  645.  
  646. /*
  647.  * convert CMS to Unix name:  "AAA     BBB     A1" becomes "aaa.bbb"
  648.  * if Uflag, don't alter case.
  649.  */
  650. static char *
  651. cms2unix(fst)
  652. tapefst *fst;
  653. {
  654.     static char name[20];
  655.     char *np = name, c;
  656.     int i;
  657.  
  658.     for (i = 0; i < 8 && (c = ETOL(fst->f_fn[i])) != ' '; i++)
  659.       *np++ = (!Uflag && isupper(c)) ? tolower(c) : c;
  660.     *np++ = '.';
  661.     for (i = 0; i < 8 && (c = ETOL(fst->f_ft[i])) != ' '; i++)
  662.       *np++ = (!Uflag && isupper(c)) ? tolower(c) : c;
  663.     *np = '\0';
  664.     if (Debug > 1)
  665.       fprintf(stderr,"cms2unix: %s\n", name);
  666.     return name;
  667. }
  668.  
  669. /*
  670.  * unix2cms: Convert unix name of /1/2/3/aaa.bbb to 16 character ebcdic string
  671.  * of "AAA     BBB     " for comparisons with fn ft.  If Uflag, don't
  672.  * alter case.
  673.  */
  674. static char *
  675. unix2cms(f)                     /* convert unix name string to CMS */
  676. char *f;
  677. {
  678.     static char name[17];
  679.     char *p,*np = name;
  680.     int i;
  681.  
  682.     f = (p = rindex(f,'/')) ? p+1 : f; /* skip leading path components */
  683.     /* filename */
  684.     for (i = 0; i < 8 && *f != '.' && *f != '\0'; i++, f++, np++)
  685.       *np = LTOE((!Uflag && islower(*f)) ? toupper(*f) : *f);
  686.     for (; i < 8; i++, np++)
  687.       *np = LTOE(' ');          /* pad out to 8 */
  688.  
  689.     /* filetype */
  690.     if (*f == '\0')
  691.       f = "file";               /* fill it in with "FILE" */
  692.     else
  693.       f++;
  694.     for (i = 0; i < 8 && *f != '.' && *f != '\0'; i++, f++, np++)
  695.       *np = LTOE((!Uflag && islower(*f)) ? toupper(*f) : *f);
  696.     for (; i < 8; i++, np++) /*  */
  697.       *np = LTOE(' ');          /* pad out to 8 */
  698.     name[16] = '\0';
  699.     return name;
  700. }
  701.  
  702. /*
  703.  * readblock - read a tape block into the Bufp buffer.
  704.  * If Fflag,  then the tape is actually a disk file created by readtape(l)
  705.  * which has a 16 bit block length prior to each data block.
  706.  * If reading straight from a tape device,  the read sys call will
  707.  * hopefully return the physical block length.  In either case,
  708.  * set *bpp to point to the beginning of the data and return the data
  709.  * length.
  710.  */
  711. static
  712. readblock(fd)                   /* read a tape block */
  713. int fd;
  714. {
  715.     int nread;
  716.  
  717.     if (Fflag)
  718.       return read_with_len(fd);
  719.     else {
  720.         nread = read(fd, Bufp, MAXBUF); /* try for the max */
  721.         if (Debug > 2)
  722.           fprintf(stderr,"physical block size %d\n", nread);
  723.         if (nread < 0)
  724.           croak(Tapename);
  725.         return nread;
  726.     }
  727. }
  728.  
  729. static
  730. read_with_len(fd)               /* read a readtape(l) format block */
  731. int fd;
  732. {
  733.     char bytes[2];
  734.     int blen;
  735.     int nread;
  736.     int amt;
  737.     char *bp;
  738.  
  739.     /* read the 2 byte readtape(l) block length prefix */
  740.     nread = read(fd, bytes, 2);
  741.     if (nread == 0)
  742.       return 0;                 /* EOF */
  743.     if (nread < 0)
  744.       croak(Tapename);
  745.     if (nread != 2)
  746.       die("%s: unexpected EOF reading block length (%d)\n",
  747.           Tapename, nread);
  748.     blen = ((unsigned)bytes[1] << 8) + (unsigned)bytes[0];
  749.     if (Debug > 2)
  750.       fprintf(stderr,"readtape block length [%d,%d] %d\n",
  751.               bytes[0], bytes[1], blen);
  752.     if (blen == 0)
  753.       return 0;         /* empty block == EOF */
  754.     if (blen > MAXBUF) {
  755.         fprintf(stderr,"%s: I/O buffer too small\n", Cmdname);
  756.         exit(1);
  757.     }
  758.  
  759.     nread = read(fd, Bufp, blen);
  760.     if (Debug > 2)
  761.       fprintf(stderr,"%d of %d read.\n", nread, blen);
  762.     if (nread < 0)
  763.       croak(Tapename);
  764.     if (nread == 0)
  765.       die("%s: unexpected EOF.\n", Tapename);
  766.     if (nread != blen)
  767.       die("%s: wrong length.  Wanted %d but got %d\n",
  768.           Tapename, blen, nread);
  769.     return blen;
  770. }
  771.  
  772. static
  773. dump(b,l)                       /* print out a block */
  774. char *b;
  775. int l;
  776. {
  777.     int i;
  778.  
  779.     for (i = 0; l-- > 0; i++) {
  780.         if (i%32 == 0)
  781.           fprintf(stderr,"\n%5d:", i);
  782.         if (i%4 == 0)
  783.           fputc(' ',stderr);
  784.         fprintf(stderr,"%02x", *b++);
  785.     }
  786.     fputc('\n',stderr);
  787. }
  788.  
  789. static
  790. prtfst(f)                       /* print out fst info */
  791. register tapefst *f;
  792. {
  793.     int i;
  794.  
  795.     fprintf(stderr,"TAPEFST:\n");
  796.     fprintf(stderr," wp: %02x%02x\n", f->f_wp[0], f->f_wp[1]);
  797.     fprintf(stderr," rp: %02x%02x\n", f->f_rp[0], f->f_rp[1]);
  798.     fprintf(stderr," mode: %c%c\n", ETOL(f->f_mode[0]), ETOL(f->f_mode[1]));
  799.     fprintf(stderr," itcnt: %d\n", IBMTOSHORT(f->f_itcnt));
  800.     fprintf(stderr," fcl: %02x%02x\n", f->f_fcl[0], f->f_fcl[1]);
  801.     fprintf(stderr," recfm: %c\n", ETOL(f->f_recfm));
  802.     fprintf(stderr," flag: %02x\n", f->f_flag);
  803.     fprintf(stderr," lrecl: %d\n", IBMTOINT(f->f_lrecl));
  804.     fprintf(stderr," blkcnt: %d\n", IBMTOSHORT(f->f_blkcnt));
  805.     fprintf(stderr," date: %02x%02x%02x%02x%02x%02x\n", f->f_date[0],
  806.             f->f_date[1], f->f_date[2], f->f_date[3], f->f_date[4],
  807.             f->f_date[5]);
  808.     fprintf(stderr," fop: %02x%02x%02x%02x\n", f->f_fop[0], f->f_fop[1],
  809.             f->f_fop[2], f->f_fop[3]);
  810.     fprintf(stderr," ablkcnt: %d\n", IBMTOINT(f->f_ablkcnt));
  811.     fprintf(stderr," areccnt: %d\n", IBMTOINT(f->f_areccnt));
  812.     fprintf(stderr," nlvl: %d\n", f->f_nlvl);
  813.     fprintf(stderr," ptrs: %d\n", f->f_ptrs);
  814.     fprintf(stderr," adate: %02x%02x%02x%02x%02x%02x\n", f->f_adate[0],
  815.             f->f_adate[1], f->f_adate[2], f->f_adate[3], f->f_adate[4],
  816.             f->f_adate[5]);
  817.     fprintf(stderr," filler: "); dump(f->f_filler,sizeof(f->f_filler));
  818.     fprintf(stderr," fn,ft,fm: ");
  819.     for (i = 0; i < 18; i++)
  820.       fputc(ETOL(f->f_fn[i]), stderr);
  821.     fputc('\n',stderr);
  822. }
  823.  
  824. #ifdef CMS
  825. /* another Waterloo C defficiency */
  826. perror(s)
  827. char *s;
  828. {
  829.     fprintf(stderr,"%s: error.\n", s);
  830. }
  831. #endif CMS
  832.  
  833. /*
  834.  * if Nametab is empty or given name matches an entry in it, return true.
  835.  */
  836. static int
  837. in_nametab(fst)
  838. tapefst *fst;
  839. {
  840.     register int i;
  841.  
  842.     if (Nametab == NULL)
  843.       return 1;
  844.     for (i = 0; i < Fcount; i++)
  845.       if (bcmp(fst->f_fn, Nametab[i].name, sizeof(Nametab[0].name)) == 0)
  846.         return 1;
  847.     return 0;
  848. }
  849.  
  850.  
  851. /*
  852.  * write files onto the tape.
  853.  * - if Fname[i] is "-Fnn" then filename is in Fname[i+1] and it should be
  854.  *   written recfm F lrecl nn.  Nn if ommitted is 80.
  855.  * - if cooked (not Iflag) then records are delimitted by newlines and
  856.  *   LTOE translation should be done.  Short recfm F records are padded
  857.  *   with blanks.  Long ones are truncated (with a message).
  858.  * - if raw (Iflag) then there are no record delimitters.  Just copy the
  859.  *   data as is to tape blocks.  For recfm V,  the data is assumed to contain
  860.  *   record lengths.  The max record length must be calculated for the fst.
  861.  * - for CMS, recfm/lrecl are obtained with a stat(), so -F is not necessary.
  862.  */
  863.  
  864. static
  865. writetape()                     /* write a cms format tape */
  866. {
  867.     int i, tapefd, mode;
  868. #undef NAME
  869. #ifdef CMS
  870.     char wlooname[50];
  871.     strcpy(wlooname,Tapename);
  872.     strcat(wlooname," (bin lrecl 65535");
  873. # define NAME wlooname
  874. #else
  875. # define NAME Tapename
  876. #endif !CMS
  877.  
  878.     mode = O_WRONLY|O_CREAT;
  879.     mode |= (Rflag)? O_APPEND : O_TRUNC;
  880.     if ((tapefd = open(NAME,mode,0644)) < 0)
  881.       croak(Tapename);
  882.     for (i = 0; i < Fcount; i++) {
  883.         int lrecl = 0;
  884.         int recfm = V;
  885.  
  886.         if (Fname[i][0] == '-' && Fname[i][1] == 'F') { /* check for -Fnn */
  887.             recfm = F;
  888.             lrecl = 80;
  889.             sscanf(&Fname[i][2],"%d", &lrecl);
  890.             ++i;
  891.         }
  892.         if (Debug)
  893.           fprintf(stderr,"Writing %s recfm %c lrecl %d\n", Fname[i],
  894.                   ETOL(recfm), lrecl);
  895.         /* check accessability and get stat info for CMSN fst use */
  896.         if (access(Fname[i], R_OK) < 0) {
  897.             fprintf(stderr,"%s: can't access.  Skipping...\n", Fname[i]);
  898.             continue;
  899.         }
  900.         tapedump(tapefd,i,lrecl,recfm);
  901.     }
  902.     close(tapefd);
  903. }
  904.  
  905. /*
  906.  * tapedump:  Dump file Fname[i] as Nametab[i] onto tapefd using given
  907.  *  recfm and lrecl.  Check Iflag to see if we want a raw or cooked dump.
  908.  */
  909. static
  910. tapedump(tapefd,i,lrecl,recfm)
  911. int tapefd,i,lrecl;
  912. char recfm;
  913. {
  914.     FILE *wf;
  915.     int nread;
  916.     int wfd;
  917. #undef NAME
  918. #ifdef CMS
  919. # define NAME wlooname
  920.     char wlooname[50];
  921.     char *cmsexpn();
  922.     fprintf(stderr,"cmsexpn: '%s'\n",cmsexpn(Fname[i]));
  923.     if (stat(cmsexpn(Fname[i]), &St) < 0) {
  924.         perror(Fname[i]);
  925.         fprintf(stderr,"Skipping...\n");
  926.         return;
  927.     }
  928.     recfm = St.st_recfm;
  929.     lrecl = St.st_lrecl;
  930.     sprintf(wlooname, "%s (recfm %c lrecl %d %s", Fname[i],
  931.             St.st_recfm, St.st_lrecl, (Iflag)?"raw bin":"text");
  932.     if (Debug)
  933.       fprintf(stderr,"Fname is %s\n", wlooname);
  934. #else
  935. # define NAME Fname[i]
  936. #endif !CMS
  937.  
  938.     Blocks = -1;                /* init tape block count */
  939.     Recs = 0;                   /* and record count */
  940.  
  941.     if (Iflag) {                /* image dump */
  942.         if ((wfd = open(NAME, O_RDONLY)) < 0) {
  943.             perror(Fname[i]);
  944.             fprintf(stderr,"Skipping...\n");
  945.             return;
  946.         }
  947. #ifndef CMS
  948.         if (recfm == F) {       /* recfm F */
  949.             int bytes = 0;
  950.  
  951.             if (Debug)
  952.               fprintf(stderr,"image dump recfm F\n");
  953.             Maxrecl = lrecl;
  954.             while ((nread = read(wfd,Bufp,TAPEDATA)) > 0) {
  955.                 bytes += nread;
  956.                 dumprec(tapefd,recfm,Bufp,nread);
  957.             }
  958.             Recs = bytes/Maxrecl;
  959.         } else {                /* recfm V */
  960.             int len;
  961.  
  962.             if (Debug)
  963.               fprintf(stderr,"image dump recfm V\n");
  964.             while (read(wfd,Bufp,2) == 2) { /* get lrecl */
  965.                 len = IBMTOSHORT(Bufp);
  966.                 if (Debug > 1)
  967.                   fprintf(stderr,"read len [%d,%d] = %d\n", Bufp[0], Bufp[1],
  968.                           len);
  969.                 Maxrecl = (Maxrecl < len) ? len : Maxrecl;
  970.                 if ((nread = read(wfd,Bufp+2,len)) != len) {
  971.                     if (Debug)
  972.                       fprintf(stderr,"len read = %d of %d\n", nread, len);
  973.                     die("%s: not a V-format image.\n",Fname[i]);
  974.                 }
  975.                 dumprec(tapefd,recfm,Bufp,len+2);
  976.             }
  977.         }
  978. #else
  979.         /* for CMS image dumps,  both recfm F and V are done the same:
  980.            Do a read with file opened raw which will return actual record
  981.            length. For V, add that length to the dump record */
  982.         while ((nread = read(wfd,Bufp+2,MAXBUF)) > 0) {
  983.             if (recfm == F)
  984.               dumprec(tapefd,recfm,Bufp+2,nread);
  985.             else {
  986.                 SHORTTOIBM(Bufp,nread);     /* length prefix */
  987.                 dumprec(tapefd,recfm,Bufp,nread+2);
  988.             }
  989.             Maxrecl = (Maxrecl < nread) ? nread : Maxrecl;
  990.         }
  991.  
  992. #endif CMS
  993.         close(wfd);
  994.     } else {                    /* cooked dump */
  995.         if ((wf = fopen(NAME,"r")) == NULL) {
  996.             perror(Fname[i]);
  997.             fprintf(stderr,"Skipping...\n");
  998.             return;
  999.         }
  1000.         while (fgets(Bufp+2,MAXBUF,wf) != NULL) {
  1001.             if ((nread = strlen(Bufp+2) - 1) <= 0) { /* min CMS rec is 1 */
  1002.                 nread = 1;
  1003.                 Bufp[2] = ' ';
  1004.             }
  1005.             if (recfm == F) {   /* pad out to lrecl */
  1006.                 pad(Bufp+2,nread,lrecl);
  1007.                 nread = lrecl;
  1008.             }
  1009.             Maxrecl = (Maxrecl < nread) ? nread : Maxrecl;
  1010.             xlate(Bufp+2,nread); /* xlate to ebcdic */
  1011.             if (recfm == V) {   /* insert record length header */
  1012.                 SHORTTOIBM(Bufp,nread);
  1013.                 dumprec(tapefd,recfm,Bufp,nread+2);
  1014.             }
  1015.             else
  1016.                 dumprec(tapefd,recfm,Bufp+2,nread);
  1017.         }
  1018.         fclose(wf);
  1019.     }
  1020.     dumpeof(tapefd,i,recfm);
  1021. }
  1022.  
  1023.  
  1024. static
  1025. pad(b,l1,l2)
  1026. char *b;
  1027. register int l1,l2;
  1028. {
  1029.     if (l1 >= l2)
  1030.       return;
  1031.     for (; l1 < l2; l1++)
  1032.       b[l1] = ' ';
  1033. }
  1034.  
  1035. static
  1036. xlate(b,l)
  1037. register char *b;
  1038. register int l;
  1039. {
  1040. #ifndef CMS
  1041.     while (l-- > 0) {
  1042.         *b = LTOE(*b);
  1043.         b++;
  1044.     }
  1045. #endif !CMS
  1046. }
  1047.  
  1048. /*
  1049.  * write record into tapebuf[].  If this fills it,  flush it to fd
  1050.  * and begin filling next tapebuf[].
  1051.  */
  1052. static
  1053. dumprec(fd,recfm,b,l)
  1054. int fd;
  1055. char recfm;
  1056. char *b;
  1057. int l;
  1058. {
  1059.     if (Blocks == -1) {         /* first time thru, initialize */
  1060.         Tbrem = TAPEDATA;       /* amount of room left in tapebuf */
  1061.         Tbp = &Tapebuf[sizeof(tapehead)];
  1062.         bcopy(CMSh,Tapebuf,sizeof(tapehead));
  1063.         ((tapehead *)Tapebuf)->fmt = recfm;
  1064.         Blocks = 0;
  1065.     }
  1066.     ++Recs;
  1067.     if (Debug > 1) {
  1068.       fprintf(stderr,"dumprec: rec %5d len = %d blockno = %d rem = %d:",
  1069.               Recs, l, Blocks, Tbrem);
  1070.       dump(b,l);
  1071.     }
  1072.     while (l > 0) {
  1073.         if (Tbrem >= l) {       /* room for the full record */
  1074.             bcopy(b,Tbp,l);
  1075.             Tbrem -= l;
  1076.             Tbp += l;
  1077.             l = 0;
  1078.         } else {                /* have to spill over */
  1079.             bcopy(b,Tbp,Tbrem); /* copy what will fit */
  1080.             dumpbuf(fd,Tapebuf,sizeof(Tapebuf));
  1081.             l -= Tbrem;         /* subtract length we could fit */
  1082.             b += Tbrem;         /* increment data pointer this much */
  1083.             Tbp = &Tapebuf[sizeof(tapehead)];
  1084.             Tbrem = TAPEDATA;
  1085.             Blocks++;
  1086.         }
  1087.     }
  1088. }
  1089.  
  1090. static
  1091. dumpeof(fd,i,recfm)
  1092. int fd,i;
  1093. char recfm;
  1094. {
  1095.     struct stat stbuf;
  1096.     struct tm *tm;
  1097.     tapehead *th = (tapehead *)Tapebuf;
  1098.     tapefst *t = (tapefst *)&Tapebuf[sizeof(tapehead)];
  1099.  
  1100.     if (Tbrem < TAPEDATA)       /* flush pending data */
  1101.       dumpbuf(fd,Tapebuf,TAPEDATA-Tbrem+sizeof(tapehead));
  1102.     if (Debug)
  1103.       fprintf(stderr,"dumpeof: blocks %d, recs %d, maxrecl %d\n",
  1104.               Blocks, Recs, Maxrecl);
  1105. #ifndef CMS
  1106.     if (stat(Fname[i],&stbuf) < 0)
  1107.       croak("dumpeof");
  1108.     /* fill in tapefst -- both old and new ("alternate") fields */
  1109.     bzero(t,sizeof(*t));
  1110.     t->f_fm[0] = t->f_mode[0] = LTOE('A');
  1111.     t->f_fm[1] = t->f_mode[1] = LTOE('1');
  1112.     SHORTTOIBM(t->f_itcnt,Recs);
  1113.     INTTOIBM(t->f_areccnt,Recs);
  1114.     t->f_recfm = th->fmt;
  1115.     th->fmt = N;                /* change to an eof header */
  1116.     INTTOIBM(t->f_lrecl,Maxrecl);
  1117.     SHORTTOIBM(t->f_blkcnt,Blocks);
  1118.     INTTOIBM(t->f_ablkcnt,Blocks);
  1119.     tm = localtime(&stbuf.st_mtime);
  1120. /* old date format is odd:  year is ebcdic,  everything else is packed */
  1121.     t->f_date[0] = 0xf0 | (tm->tm_year/10)&0x0f; /* ebcdic year tens */
  1122.     t->f_date[1] = 0xf0 | (tm->tm_year%10)&0x0f; /* ebcdic year ones */
  1123.     t->f_adate[0] = PACK(tm->tm_year);
  1124.     t->f_adate[1] = t->f_date[2] = PACK(tm->tm_mon+1);
  1125.     t->f_adate[2] = t->f_date[3] = PACK(tm->tm_mday);
  1126.     t->f_adate[3] = t->f_date[4] = PACK(tm->tm_hour);
  1127.     t->f_adate[4] = t->f_date[5] = PACK(tm->tm_min);
  1128.     t->f_adate[5] = PACK(tm->tm_sec);
  1129.     bcopy(Nametab[i].name,t->f_fn,sizeof(Nametab[0].name));
  1130. #else
  1131.     /* stat already done into St */
  1132.     bzero(t,sizeof(*t));
  1133.     t->f_fm[0] = t->f_mode[0] = St.st_fmode[0];
  1134.     t->f_fm[1] = t->f_mode[1] = St.st_fmode[1];
  1135.     SHORTTOIBM(t->f_itcnt,St.st_size);
  1136.     INTTOIBM(t->f_areccnt,St.st_size);
  1137.     t->f_recfm = th->fmt;
  1138.     th->fmt = N;                /* change to an eof header */
  1139.     INTTOIBM(t->f_lrecl,Maxrecl);
  1140.     SHORTTOIBM(t->f_blkcnt,Blocks);
  1141.     INTTOIBM(t->f_ablkcnt,Blocks);
  1142. /* old date format is odd:  year is ebcdic,  everything else is packed */
  1143.     bcopy(St.st_mtime,t->f_adate,sizeof(t->f_adate));
  1144.     bcopy(&t->f_adate[1],&t->f_date[2], sizeof(t->f_date)-2);
  1145.     t->f_date[0] = HI(t->f_adate[0]) | 0xf0;
  1146.     t->f_date[1] = LO(t->f_adate[0]) | 0xf0;
  1147.     bcopy(St.st_fname,t->f_fn,sizeof(St.st_fname)+sizeof(St.st_ftype));
  1148. #endif CMS
  1149.     dumpbuf(fd,Tapebuf,sizeof(tapehead)+sizeof(tapefst));
  1150.     if (Tflag)
  1151.       prinfo(t);
  1152. }
  1153.  
  1154. static
  1155. dumpbuf(fd,b,l)
  1156. int fd;
  1157. char *b;
  1158. int l;
  1159. {
  1160.     if (Debug > 1)
  1161.       fprintf(stderr,"dumping block %d, len %d.\n", Blocks, l);
  1162.     if (Fflag) {                /* write readtape(l) count */
  1163.         u_char clen[2];
  1164.  
  1165.         clen[1] = l >> 8;       /* vax byte order */
  1166.         clen[0] = l & 0xff;
  1167.         if (write(fd,clen,2) < 0)
  1168.           croak("dumpbuf");
  1169.     }
  1170.     if (write(fd,b,l) < 0)      /* flush it */
  1171.       croak("dumpbuf");
  1172.     if (Debug > 2) {
  1173.         fprintf(stderr,"data: ");
  1174.         dump(b,l);
  1175.     }
  1176. }
  1177.  
  1178. #ifdef CMS
  1179. static char *
  1180. cmsexpn(f)                      /* fudge file name for wloo C stat(), etc. */
  1181. char *f;
  1182. {
  1183.     static char e[21];
  1184.     char *ep = e;
  1185.     int i;
  1186.  
  1187.     /* filename */
  1188.     for (i = 0; i < 8 && isalnum(*f); i++,f++,ep++)
  1189.       *ep = islower(*f)? toupper(*f) : *f;
  1190.     *ep++ = ' ';
  1191.     while (isalnum(*f))         /* skip more than 8 char filename */
  1192.       f++;
  1193.     if (*f == '\0' || *f == '(') { /* filetype missing */
  1194.         strcat(ep,"FILE");
  1195.         return e;
  1196.     }
  1197.     /* filetype */
  1198.     for (f++, i = 0; i < 8 && isalnum(*f); i++,f++,ep++)
  1199.       *ep = islower(*f)? toupper(*f) : *f;
  1200.     *ep++ = ' ';
  1201.     while (isalnum(*f))         /* skip more than 8 char filetype */
  1202.       f++;
  1203.     if (*f == '\0' || *f == '(') { /* filemode missing */
  1204.         ep[-1] = '\0';             /* clobber the trailing blank */
  1205.         return e;
  1206.     }
  1207.     /* filemode */
  1208.     for (f++, i = 0; i < 2 && isalnum(*f); i++,f++,ep++)
  1209.       *ep = islower(*f)? toupper(*f) : *f;
  1210.     *ep = '\0';
  1211.     return e;
  1212. }
  1213. #endif CMS
  1214.  
  1215.  
  1216. /*
  1217.  * various ways to die
  1218.  */
  1219. static
  1220. die(s,a1,a2,a3,a4,a5)
  1221. char *s;
  1222. {
  1223.     fprintf(stderr,s,a1,a2,a3,a4,a5);
  1224.     exit(1);
  1225. }
  1226.  
  1227. static
  1228. croak(s)
  1229. char *s;
  1230. {
  1231.     perror(s);
  1232.     exit(1);
  1233. }
  1234.  
  1235.