/* CUTAPE */ /* Write labelled tapes in either of two formats: . ANSI label ASCII format "D" (ANSI X3.27-1978, Level 3) . IBM OS standard label EBCDIC format "VB" */ /* Authors: Jeff Damens, Grace Lee, Frank da Cruz Columbia University Center for Computing Activities 612 W 115 Street New York NY 10025 Copyright (C) 1984, Trustees of Columbia University in the City of New York May be copied, reproduced, or adapted without permission except for explicitly commercial purposes. */ /* * write an ebcdic standard labeled tape * jd, 9/84 * * Added ansi tape capability, numerous improvements * grace, 10/84 * * Added some performance optimizations * jd, 1/85 * * Made ANSI tape show ANSI version 3 in VOL1, column 80 * fdc, 2/86 * * Added -f option to select tape drive, help message, ... * fdc, 5/87 * * Merged a-to-e translation table into this source, so there's only * one source file. * fdc, 5/88 * */ #include #include #include #include #include #include #include #include #define TRUE 1 #define FALSE 0 #define CRDATE 6 /* size of creation date field */ /* flag for ebcdic and ascii tape */ #define EBC 0 #define ASC 1 /* * Default blocksize and record length, and default tape drive to use. */ #define BLKSIZE 8192 #define LRECL 300 #ifndef DEFTAPE #define DEFTAPE "/dev/rmt12" #endif /* global variables */ int fd,filno=1; int blksize = BLKSIZE, lrecl = LRECL; int tmp,flag = EBC; int v1done = FALSE; /* no vol1 lbl written yet */ char tape = 'e'; char *volid = "KERMIT", *owner = ""; /* default label and owner */ char *tapeid = DEFTAPE; /* default tape drive */ struct plain { char c[80]; }; /* dummy template */ /* * template for vol1 label * */ struct vol1 { /* volume label for ebcdic tape */ char labid[4], /* always VOL1 */ volser[6], /* volume label */ junk1[31], /* reserved */ owner[10], /* volume owner */ junk2[29]; }; struct avol1 { /* volume label for ansi tape */ char labid[4], /* always VOL1 */ volser[6], /* volume label */ junk1[27], /* reserved */ owner[14], /* volume owner */ junk2[28], /* (fdc) reserved */ ansver[1]; }; /* (fdc) ANSI version */ /* * template for HDR1/EOF1 label * */ struct lab1 { char labid[4], /* hdr1 or eof1 */ dsn[17], /* dataset name */ volser[6], /* vol ser */ volseq[4], /* volume sequence (0001) */ dataseq[4], /* dataset sequence number */ gen[4], /* generation # (blank) */ genver[2], /* version of generation (blank) */ credate[6], /* creation date */ expdate[6], /* expiration date */ secure, /* security (0) */ blkcnt[6], /* block count */ system[13], /* system code */ junk[7]; }; /* reserved */ /* * template for HDR2/EOF2 label * */ struct lab2 { /* header label for ebcdic tape */ char labid[4], /* hdr2 or eof2 */ recfm, /* record format */ blksize[5], /* block length */ lrecl[5], /* record length */ density, /* tape density */ dsp, /* dataset position (0) */ jsi[17], /* job step identification (ha) */ trt[2], /* tape recording technique (blank) */ cc, /* control char (blank) */ res, /* reserved */ blkattr, /* blocking type */ res2[41]; }; /* more reserved */ struct alab2 { /* header label for ascii tape */ char labid[4], /* hdr2 or eof2 */ recfm, /* record format */ blksize[5], /* block length */ lrecl[5], /* record length */ junk1[35], /* reserved for system use */ bufoff[2], /* buffer-offset length */ junk2[28]; }; /* reserved */ union label { struct vol1 v1; struct avol1 av1; struct lab1 l1; struct lab2 l2; struct alab2 al2; struct plain p }; typedef union label *Label; /* ASCII/EBCDIC translation table, follows the IBM System/370 Reference Summary, GX20-1850-6, File No. S370/4300-01 */ char atoe[] = { 0, /* 0 */ 1, /* 1 */ 2, /* 2 */ 3, /* 3 */ 55, /* 4 */ 45, /* 5 */ 46, /* 6 */ 47, /* 7 */ 22, /* 8 */ 5, /* 9 */ 37, /* 10 */ 11, /* 11 */ 12, /* 12 */ 13, /* 13 */ 14, /* 14 */ 15, /* 15 */ 16, /* 16 */ 17, /* 17 */ 18, /* 18 */ 108, /* 19 */ 60, /* 20 */ 61, /* 21 */ 108, /* 22 */ 108, /* 23 */ 24, /* 24 */ 25, /* 25 */ 63, /* 26 */ 39, /* 27 */ 34, /* 28 */ 108, /* 29 */ 108, /* 30 */ 108, /* 31 */ 64, /* 32 */ 90, /* 33 */ 127, /* 34 */ 123, /* 35 */ 91, /* 36 */ 108, /* 37 */ 80, /* 38 */ 125, /* 39 */ 77, /* 40 */ 93, /* 41 */ 92, /* 42 */ 78, /* 43 */ 107, /* 44 */ 96, /* 45 */ 75, /* 46 */ 97, /* 47 */ 240, /* 48 */ 241, /* 49 */ 242, /* 50 */ 243, /* 51 */ 244, /* 52 */ 245, /* 53 */ 246, /* 54 */ 247, /* 55 */ 248, /* 56 */ 249, /* 57 */ 122, /* 58 */ 94, /* 59 */ 76, /* 60 */ 126, /* 61 */ 110, /* 62 */ 111, /* 63 */ 124, /* 64 */ 193, /* 65 */ 194, /* 66 */ 195, /* 67 */ 196, /* 68 */ 197, /* 69 */ 198, /* 70 */ 199, /* 71 */ 200, /* 72 */ 201, /* 73 */ 209, /* 74 */ 210, /* 75 */ 211, /* 76 */ 212, /* 77 */ 213, /* 78 */ 214, /* 79 */ 215, /* 80 */ 216, /* 81 */ 217, /* 82 */ 226, /* 83 */ 227, /* 84 */ 228, /* 85 */ 229, /* 86 */ 230, /* 87 */ 231, /* 88 */ 232, /* 89 */ 233, /* 90 */ 173, /* 91 */ 0xe0, /* 92 */ 189, /* 93 */ 95, /* 94 */ 109, /* 95 */ 0x79, /* 96 */ 129, /* 97 */ 130, /* 98 */ 131, /* 99 */ 132, /* 100 */ 133, /* 101 */ 134, /* 102 */ 135, /* 103 */ 136, /* 104 */ 137, /* 105 */ 145, /* 106 */ 146, /* 107 */ 147, /* 108 */ 148, /* 109 */ 149, /* 110 */ 150, /* 111 */ 151, /* 112 */ 152, /* 113 */ 153, /* 114 */ 162, /* 115 */ 163, /* 116 */ 164, /* 117 */ 165, /* 118 */ 166, /* 119 */ 167, /* 120 */ 168, /* 121 */ 169, /* 122 */ 192, /* 123 */ 79, /* 124 */ 208, /* 125 */ 0xa1, /* 126 */ 108 /* 127 */ }; char curdate[CRDATE+1]; /* current date for ascii tape */ /* * strip the path components from a filename. Returns a pointer to the * bare file name, so it can be written to a label. * */ char * basename(x) char *x; { char *p,*rindex();; if ((p = rindex(x,'/')) == NULL) return(x); return(p+1); } /* * convert a filename so that it can be written to a tape label. * any path is stripped, and the name is uppercased. Returns a pointer * to the new name. The name is changed in place. * */ char * cvtfnam(x) char *x; { char *fnptr; for (x = basename(x), fnptr = x; *x != '\0'; x++) if (islower(*x)) *x = toupper(*x); return(fnptr); } /* * clear a label by writing spaces in all the columns. * */ clrlabel(l) Label l; { int i; for (i=0; i<80; i++) l->p.c[i] = ' '; } /* * write a numeric field to a label. Pass the destination address, * the number to be written and the # of columns the number is to occupy. * if zfill is TRUE, we pad with zeros (otherwise blanks). * */ putnfld(dest,num,places,zfill) register char *dest; register int num; int places,zfill; { register char *d,f; d = dest + places - 1; /* end of buffer */ do { *d-- = (num % 10) + '0'; num /= 10; } while (num != 0); f = zfill ? '0' : ' '; while (d >= dest) *d-- = f; } /* * write a string field to a label. Pass the destination and source * addresses, the # of places to be filled. * */ putsfld(dest,src,places) register char *dest,*src; register int places; { register char c; while (c = *src) { *dest++ = c; src++; places--; } while (places--) *dest++ = ' '; } /* * write a label to the given file descriptor. Pass the file descriptor * and a pointer to the (80 column) label. If the tape is ebcdic, * the label is converted to ebcdic before it is written to the tape. * Otherwise, it is written as ascii. * */ wrlab(fd,blk,typ) int fd,typ; char *blk; { char eblk[80]; if (typ == EBC) { cvtebc(eblk,blk); if (write(fd,eblk,80) != 80) wrerr("Error writing label - possibly off end of tape"); } else if (write(fd,blk,80) != 80) wrerr("Error writing label - possibly off end of tape"); } /* * check if the character is alphanumeric * if not, replace it by 'X' * */ checkfn(fn) char *fn; { int period = 0; register int c; for (; c = *fn; fn++) if ((c == '.' && ++period > 1) || (c != '.' && !isalnum(c))) *fn = 'X'; } /* * write a tape mark to the tape * */ wtm(fd) int fd; { struct mtop m; m.mt_op = MTWEOF; m.mt_count = 1; if (ioctl(fd,MTIOCTOP,(char *) &m) == -1) perror("mtioctl"); } /* * rewind and unload the tape * */ unl(fd) int fd; { struct mtop m; m.mt_op = MTOFFL; if (ioctl(fd,MTIOCTOP,(char *) &m) == -1) perror("unload failed"); } /* * rewind the tape * */ rew(fd) int fd; { struct mtop m; m.mt_op = MTREW; if (ioctl(fd,MTIOCTOP,(char *) &m) == -1) perror("rewind failed"); } /* * copy the source string to the destination, converting to ebcdic. * this stops on a null byte. * */ cvtebc(dst,src) char *dst,*src; { while (*dst++ = atoe[*src++]); } /* * write an ebcdic vol1 label, passed a tape file descriptor and the * volume label and owner strings * */ wvol1(fd,volser,owner,flag) int fd,flag; char *volser,*owner; { union label l; clrlabel(&l); /* init all fields to spaces */ strncpy(l.v1.labid,"VOL1",4); putsfld(l.v1.volser,cvtfnam(volser),6); /* put in volser */ l.v1.junk1[0] = '0'; /* must be 0 (?) */ putsfld(l.v1.owner,cvtfnam(owner),10); /* copy in owner name */ wrlab(fd,(char *) &l,flag); } /* * write a vol1 label on ascii tape, passed a tape file descriptor and the * volume label and owner strings * */ wavol1(fd,volser,owner,flag) int fd,flag; char *volser,*owner; { union label l; char *a; clrlabel(&l); /* init all fields to spaces */ strncpy(l.av1.labid,"VOL1",4); putsfld(l.av1.volser,cvtfnam(volser),6); /* put in volser */ l.av1.junk1[0] = ' '; putsfld(l.av1.owner,cvtfnam(owner),14); /* copy in owner name */ strncpy(l.av1.ansver,"3",1); /* ANSI version */ wrlab(fd,(char *) &l,flag); } /* * write a HDR1 or EOF1 label to the given file descriptor. * labtyp should be either HDR1 or EOF1, * dsn the filename to be written to the label, * volser the internal tape label, * dsseq the position of this file on the tape, * blkcnt 0 for a HDR1 label or the number of tape blocks in the file for EOF1. * */ wlab1(fd,labtyp,dsn,volser,dsseq,blkcnt,flag) int fd,dsseq,blkcnt,flag; char *labtyp,*dsn,*volser; { char * getansidate(); union label l; clrlabel(&l); strncpy(l.l1.labid,labtyp,4); checkfn(dsn); putsfld(l.l1.dsn,dsn,17); putsfld(l.l1.volser,cvtfnam(volser),6); putnfld(l.l1.volseq,1,4,TRUE); /* volseq is always 1 */ putnfld(l.l1.dataseq,dsseq,4,TRUE); /* dataset sequence # */ if (flag == ASC) { putsfld(l.l1.gen,"0001",4); /* generation is always 1 */ putsfld(l.l1.genver,"00",2); /* generation version is 0 */ } putsfld(l.l1.credate,curdate,6); putsfld(l.l1.expdate,curdate,6); if (flag == EBC) l.l1.secure = '0'; /* no security */ else l.l1.secure = ' '; putnfld(l.l1.blkcnt,blkcnt,6,TRUE); wrlab(fd,(char *) &l,flag); } /* * write a HDR2/EOF2 label to the given tape file descriptor. * labtyp should be either HDR1 or EOF2. * blksiz and lrecl should be the tape blocksize and maximum record * length of the file. * record format currently forced to VB, density to 1600 BPI. * */ wlab2(fd,labtyp,blksiz,lrecl,flag) /* ebcdic tape */ int fd,blksiz,lrecl,flag; char *labtyp; { union label l; clrlabel(&l); strncpy(l.l2.labid,labtyp,4); l.l2.recfm = 'V'; /* variable length records */ putnfld(l.l2.blksize,blksiz,5,TRUE); putnfld(l.l2.lrecl,lrecl,5,TRUE); l.l2.density = '3'; /* 1600 BPI */ l.l2.dsp = '0'; l.l2.blkattr = 'B'; /* blocked records */ wrlab(fd,(char *) &l,flag); } /* * write an ansi HDR2/EOF2 label. * The only currently supported record format is 'D'. * */ walab2(fd,labtyp,blksiz,lrecl,flag) /* ascii tape */ int fd,blksiz,lrecl,flag; char *labtyp; { union label l; clrlabel(&l); strncpy(l.al2.labid,labtyp,4); l.al2.recfm = 'D'; /* variable length records */ putnfld(l.al2.blksize,blksiz,5,TRUE); putnfld(l.al2.lrecl,lrecl,5,TRUE); putsfld(l.al2.bufoff,"00",2); wrlab(fd,(char *) &l,flag); } /* * write a file to the given tape file descriptor. * this writes the header and trailer labels and the contents of * the file, handling all necessary blocking. The file is written * to the tape in V format. * Also writes the file number and file name to standard output, * for listing purposes. * * fname should be the actual (UNIX) filename. * blksiz and lrecl should be the desired tape blocksize and max. record length * volser is the internal tape label * filno is the position of this file on the tape. * */ wrfil(fd,fname,blksiz,lrecl,volser,filno,flag,length) int fd,blksiz,lrecl,filno,flag,length; char *fname,*volser; { static char *blkbuf = NULL,*recbuf = NULL; static int oblksiz, olrecl; FILE *fp; char *malloc(),*blkptr; register char *recptr,*ufname; int reccnt,blklen,reclen,blkcnt,i; if ((fp = fopen(fname,"r")) == NULL) { fprintf(stderr,"Can't open %s\n",fname); return; } ufname = cvtfnam(fname); /* convert a filename */ printf("%3d %s\n",filno,ufname); blkcnt = 0; if (blkbuf == NULL || oblksiz < blksiz) { if (blkbuf) free(blkbuf); /* free any old buffer */ if ((blkbuf = malloc(blksiz)) == NULL) fatal("Can't alloc block buffer"); oblksiz = blksiz; /* remember size of buffer */ } if (recbuf == NULL || olrecl < lrecl) { if ((recbuf = malloc(lrecl)) == NULL) fatal("Can't alloc record buffer"); olrecl = lrecl; /* remember buffer size */ } wlab1(fd,"HDR1",ufname,volser,filno,blkcnt,flag); if (flag == EBC) wlab2(fd,"HDR2",blksiz,lrecl,flag); else walab2(fd,"HDR2",blksiz,lrecl,flag); wtm(fd); reccnt = 1; blkptr = blkbuf + length; /* place where data starts */ blklen = length; /* length of block */ while (!feof(fp)) { register int c,cnt; cnt = lrecl; recptr = recbuf; while (--cnt && (c = getc(fp)) != EOF && c != '\n') *recptr++ = c; *recptr = '\0'; /* tie off record */ reclen = lrecl - cnt - 1; if (c != '\n' && c != EOF) { while ((c = getc(fp)) != EOF && c != '\n'); fprintf(stderr,"In file %s, record %d truncated\n",ufname,reccnt); } if (reclen == 0) { if (c == EOF) break; /* at end of file, forget it */ if (flag == EBC) { recbuf[0] = ' '; /* cant write empty records */ reclen = 1; } } reccnt++; reclen += 4; /* count actual record length */ if (blklen + reclen > blksiz) /* buffer going to be full? */ { /* yes, empty buffer first */ if (flag == EBC) { blkbuf[0] = blklen >> 8; blkbuf[1] = blklen & 0xff; /* fill in block length */ blkbuf[2] = blkbuf[3] = 0; } else /* fill ansi block to mult of 4 */ while (blklen % 4 != 0) *blkptr++ = '^', blklen++; if (write(fd,blkbuf,blklen) != blklen) wrerr("Error writing block - possibly off end of tape"); blkcnt++; blkptr = blkbuf + length; /* init ptr again */ blklen = length; } blklen += reclen; /* include this record */ if (flag == EBC) { /* record length in binary */ *blkptr++ = reclen >> 8; /* high order */ *blkptr++ = reclen & 0xff; /* low order */ *blkptr++ = 0; *blkptr++ = 0; } /* two filler bytes... */ else { putnfld(blkptr,reclen,4,TRUE); /* record length in */ blkptr += 4; } reclen -= 4; /* subtract overhead for loop */ if (flag == EBC) for (recptr = recbuf; reclen > 0; reclen--) *blkptr++ = atoe[*recptr++]; else /* dupl loop so dont test each chr */ for (recptr = recbuf; reclen > 0; reclen--) *blkptr++ = *recptr++; } /* end of while */ if (blklen > length) /* anything in last block? */ { if (flag == EBC) { blkbuf[0] = blklen >> 8; blkbuf[1] = blklen & 0xff; /* fill in block length */ blkbuf[2] = blkbuf[3] = 0;} else /* pad ansi tape to mult of 4 */ while (blklen % 4 != 0) *blkptr++ = '^', blklen++; if (write(fd,blkbuf,blklen) != blklen) wrerr("Error writing block - possibly off end of tape"); blkcnt++; } wtm(fd); wlab1(fd,"EOF1",ufname,volser,filno,blkcnt,flag); if (flag == EBC) wlab2(fd,"EOF2",blksiz,lrecl,flag); else walab2(fd,"EOF2",blksiz,lrecl,flag); wtm(fd); fclose(fp); } /* * main routine - handles command parsing, writes VOL1 label. * */ main(argc,argv) int argc; char *argv[]; { if (argc < 2) usage(); getansidate(curdate); /* get date for labels */ while (*++argv) if (**argv == '-') /* Parse options */ switch((*argv)[1]) { case 'v' : volid = *++argv; break; case 'o' : owner = *++argv; break; case 'b' : if ((tmp = atoi(*++argv)) < 10) printf("illegal blocksize\n"); else blksize = tmp; break; case 'l' : if ((tmp = atoi(*++argv)) < 10) printf("illegal lrecl\n"); else lrecl = tmp; break; case 't' : if (**++argv == 'e') { tape = 'e'; flag = EBC; } else if (**argv == 'a') { tape = 'a'; flag = ASC; } else printf("illegal tape selection\n"); break; case 'f' : tapeid = *++argv; break; default : usage(); } else { if (!v1done) { if ((fd = open(tapeid,1)) == -1) fatal("can't open tape"); rew(fd); if (flag != EBC) blksize = (blksize + 3) & ~3; /* if ansi, blksize is mult of 4 */ printf("Tape %s, volume %s, owner %s\n",tapeid,volid,owner); printf("recfm %s, blocksize %d, lrecl %d, type %c\n\n", flag == EBC ? "VB" : "D",blksize,lrecl,tape); if (flag == EBC) wvol1(fd,volid,owner,flag); else wavol1(fd,volid,owner,flag); v1done = TRUE; } filno = wrdir(fd,*argv,blksize,lrecl,volid,filno,flag); } wtm(fd); close(fd); if ((fd = open(tapeid,0)) == -1) fatal("can't reopen tape"); rew(fd); close(fd); } /* * write a directory to the tape. If the path is just a regular file, * write it to the tape. Otherwise open the directory and recursively * write all the files in it to the tape. Returns an updated filno, eg * filno + # of files written. * */ int wrdir(fd,path,blksize,lrecl,volid,filno,flag) int fd,blksize,lrecl,filno,flag; char *path,*volid; { struct stat sbuf; DIR *dp; struct direct *dirent; char tmpname[300]; int length; if (stat(path,&sbuf) == -1) { fprintf(stderr,"%s not found\n",path); return(filno); } if ((sbuf.st_mode & S_IFREG) != 0) { /* regular file, just put on tape */ if (flag == EBC) { /* ebcdic tape */ length = 4; wrfil(fd,path,blksize,lrecl,volid,filno,flag,length); return(filno+1); } else { /* ascii tape */ length = 0; wrfil(fd,path,blksize,lrecl,volid,filno,flag,length); return(filno+1); } } if ((sbuf.st_mode & S_IFDIR) != 0) { /* directory file */ if ((dp = opendir(path)) == NULL) { fprintf(stderr,"Can't read directory "); perror(path); return(filno); } while ((dirent = readdir(dp)) != NULL) { if (dirent->d_name[0] == '.') continue; sprintf(tmpname,"%s/%s",path,dirent->d_name); filno = wrdir(fd,tmpname,blksize,lrecl,volid,filno,flag); } closedir(dp); return(filno); } else { fprintf(stderr,"%%%s not regular file or directory, skipping\n", path); return(filno); } } /* * fatal error handler - prints the given message and the last * system error, then aborts so that we get a core dump. * */ fatal(msg) char *msg; { perror(msg); abort(); } /* * wrerr - print a tape write error and exit with a non-zero condition * code (but don't core dump). * */ wrerr(msg) char *msg; { fprintf(stderr,"%s\n",msg); printf("?Error: %s\n",msg); /* print to error and listing */ exit(10); } /* getansidate -- return the current date in ansi format * * Ansi dates are strings of the form " yyddd" where * yy = the year and ddd = the day in the year. There must * be an initial blank. */ char * getansidate(curdate) char *curdate; { time_t now, time(); struct tm *timep, *localtime(); now = time(NULL); timep = localtime(&now); sprintf(curdate," %02d%03d",timep->tm_year,timep->tm_yday); return(curdate); } usage() { printf("usage: cutape options files\n"); printf("options: vobltf\n"); printf("v - volume id, default %s\n",volid); printf("o - tape owner id, default (none)\n"); printf("b - blocksize, default %d\n",blksize); printf("l - maximum logical record length, default %d\n",lrecl); printf("t - type: a for ANSI format D, e for ebcdic OS SL, "); printf("default %c\n",tape); printf("f - tape drive to use, default %s\n",tapeid); exit(1); }