vmsbackup-dist/ 40755 765 144 0 6272202376 12761 5ustar kingdonusersvmsbackup-dist/README100644 765 144 6272 6272201413 13734 0ustar kingdonusersThis progam reads a VMS backuptape. WARNING: This version is still well short of the full features of BACKUP. Hopefully this file should somewhat document the various quirks and limitations (although it doesn't do as good a job of describing the things which are completely missing). If you have patches to send in or reports of success and/or failure, I guess for now the place to send them is kingdon@cyclic.com. In the future there is a good chance there will be some mailing list for VMSBACKUP or for "Free-VMS file utilities" or some such category; you might want to see if http://www.lp.se/free-vms/ has something more up-to-date than this paragraph (as of this writing the only thing there is that BACKUP is listed in the task list). To find out what version you have on unix use the -V option. On VMS, ANALYZE/IMAGE/HEADER VMSBACKUP.EXE and look for the part where it says "image file identification:". In the sources look at getoptmain.c or build.com (which will have the same version number, unless someone messed up). To build on unix, "make". To build on VMS, "@build". Known bugs include: * Poor handling of redundancy groups. If you create a saveset with /GROUP_SIZE=0, then vmsbackup will be do just fine, but if the group size is nonzero (the default), then vmsbackup/LIST will tend to list files twice. * On VMS systems probably cannot read tapes (only savesets in .BCK files on disk). Maybe it could be done if you mount the tape non-foreign, but I don't know of anyone who has tried that. Reading tapes does work on non-VMS systems (at least with version 3.0; presumably with more recent versions too). * Dates are not printed on non-VMS systems; where the dates would be there is nothing. This can be fixed if someone wants to write a portable equivalent for SYS$ASCTIM. * We do not yet print dates for files (just the date for the saveset itself). * One cannot specify a file to list to, as in /LIST=file. * /LIST/FULL prints much less than BACKUP; I haven't decoded most of the brh_dol_k_file info yet. * Some of the record formats and record attributes are not printed using the same text that BACKUP uses. This is just a matter of going through and figuring out what it should be in each case (would synergize nicely with testsuite, below, because then we could just run the testsuite on vmsbackup and on BACKUP and make sure they agree). Extraction is even worse; anything other than a text file is unlikely to get extracted in any useful manner. TODO: * Anyone want to write a testsuite? * Implement lots more features. Open questions: * Is there a documented standard for what savesets look like? From DEC or from http://www.bbc.com or from anyone else? History: The tape program is orginally written by John Douglas Carey and the pattern matching routine by some unknown on the net. The remote tape option use the rmtlib from mod.sources. A good way to archive remotetape access for users with only a local account is to create a "netwide" user tar and let the remote tape programs do suid to user tar. The program is tested on vax and sun. Sven-Ove Westberg Lulea University of Technology S-951 87 Lulea, Sweden UUCP: sow@luthcad.UUCP UUCP: {decvax,philabs,seismo}!mcvax!enea!luthcad!sow vmsbackup-dist/vmsbackup.1100644 765 144 6770 6272175612 15147 0ustar kingdonusers.TH VMSBACKUP 1 .SH NAME vmsbackup \- read a VMS backup tape .SH SYNOPSIS .B vmsbackup .B \-{tx}[cdevwB][s setnumber][f tapefile][b blocksize] [ name ... ] .SH DESCRIPTION .I vmsbackup reads a VMS generated backup tape, converting the files to Unix format and writing the files to disc. The default operation of the program is to go through an entire tape, extracting every file and writing it to disc. This may be modified by the following options. .TP 8 .B b blocksize Use blocksize as the blocksize to read the saveset with. .TP 8 .B B Extract files in binary mode. .TP 8 .B c Use complete filenames, including the version number. A colon and the octal version number will be appended to all filenames. A colon, rather than a semicolon, is used since the Unix Shell uses the semicolon as the line separator. Using a colon prevents the user from having to escape the semicolon when referencing the filename. This option is useful only when multiple versions of the same file are on a single tape or when a file of the same name already exists in the destination directory. The default is to ignore version numbers. .TP 8 .B d use the directory structure from VMS, the default value is off. .TP 8 .B e Process all filename extensions. Since this program is mainly intended to move source code and possibly data from a DEC system to a Unix system, the default is to ignore all files whose filename extension specifies system dependent data. The file types which will be ignored, unless the .B e option is specified, are .IP "" 10 exe VMS executable file .br lib VMS object library file .br obj RSX object file .br odl RSX overlay description file .br olb RSX object library file .br pmd RSX post mortem dump file .br stb RSX task symbol table file .br sys RSX bootable system file .br tsk RSX executable task file .PP .TP 8 .B f Use the next argument in the command line as the tape device to be used, rather than the default. .sp If vmsbackup is compiled with the remote tape option and the file name has the form .IR system [. user ]:/dev/??? .I vmsbackup will use the tape drive /dev/??? on the remote system .IR system , via .IR rsh (1), and .IR rmt (8). The optional .I user portion of the pathname specifies the login name to use on the remote system. If it is not supplied, the current user's login name will be used. In all the cases, the user must have the appropriate permissions on the remote machine, in order to use this facility. The default is .I /dev/rmt8 (drive 0, raw mode, 1600 bpi). This must be a raw mode tape device. .TP 8 .B s saveset Process only the given saveset number. .TP 8 .B t Produce a table of contents (a directory listing) on the standard output of the files on tape. .TP 8 .B v Verbose output. Normally .I vmsbackup does its work silently. The verbose option will cause the filenames of the files being read from tape to disk to be output on the standard output. .TP 8 .B w .I vmsbackup prints the action to be taken followed by file name, then wait for user confirmation. If a word beginning with `y' is given, the action is done. Any other input means don't do it. .TP 8 .B x extract the named files from the tape. .TP 8 The optional .I name argument specifies one or more filenames to be searched for specifically on the tape and only those files are to be processed. The name may contain the usal sh(1) meta-characters *?![] \nnn. .SH FILES /dev/rmt\fIx\fP .SH SEE ALSO rmtops(3) .SH BUGS The filename match uses the complete VMS file names. .SH AUTHOR John Douglas Carey .br Sven-Ove Westberg vmsbackup-dist/Makefile100644 765 144 1476 6250172260 14520 0ustar kingdonusers# # REMOTE=#-DREMOTE # -DREMOTE use remote tape CFLAGS= $(REMOTE) -g LFLAGS= LIBS= #-lrmt # remote magtape library OWNER=tar # user for remote tape access MODE=4755 BINDIR=/usr/local/bin MANSEC=l MANDIR=/usr/man/man$(MANSEC) DISTFILES=README vmsbackup.1 Makefile vmsbackup.c match.c NEWS \ build.com dclmain.c getoptmain.c vmsbackup.cld vmsbackup.h \ sysdep.h # vmsbackup: vmsbackup.o match.o getoptmain.o cc $(LFLAGS) -o vmsbackup vmsbackup.o match.o getoptmain.o $(LIBS) install: install -m $(MODE) -o $(OWNER) -s vmsbackup $(BINDIR) cp vmsbackup.1 $(MANDIR)/vmsbackup.$(MANSEC) clean: rm -f vmsbackup *.o core shar: shar -a $(DISTFILES) > vmsbackup.shar dist: rm -rf vmsbackup-dist mkdir vmsbackup-dist for i in $(DISTFILES); do \ ln $${i} vmsbackup-dist; \ done tar chf vmsbackup.tar vmsbackup-dist vmsbackup-dist/vmsbackup.c100644 765 144 64434 6272200126 15237 0ustar kingdonusers/* * * Title: * Backup * * Decription: * Program to read VMS backup tape * * Author: * John Douglas CAREY. (version 2.x, I think) * Sven-Ove Westberg (version 3.0) * See ChangeLog file for more recent authors. * * Net-addess (as of 1986 or so; it is highly unlikely these still work): * john%monu1.oz@seismo.ARPA * luthcad!sow@enea.UUCP * * * Installation (again, as of 1986): * * Computer Centre * Monash University * Wellington Road * Clayton * Victoria 3168 * AUSTRALIA * */ /* Does this system have the magnetic tape ioctls? The answer is yes for most/all unices, and I think it is yes for VMS 7.x with DECC 5.2 (needs verification), but it is no for VMS 6.2. */ #ifndef HAVE_MT_IOCTLS #define HAVE_MT_IOCTLS 1 #endif #ifdef HAVE_UNIXIO_H /* Declarations for read, write, etc. */ #include #else #include #include #endif #include #include #include #include #include #include #if HAVE_MT_IOCTLS #include #include #endif #ifdef REMOTE #include #include #endif #include #ifndef __vax /* The help claims that mkdir is declared in stdlib.h but it doesn't seem to be true. AXP/VMS 6.2, DECC ?.?. On the other hand, VAX/VMS 6.2 seems to declare it in a way which conflicts with this definition. This is starting to sound like a bad dream. */ int mkdir (); #endif #include "vmsbackup.h" #include "sysdep.h" int match (); char *strlocase (); /* Byte-swapping routines. Note that these do not depend on the size of datatypes such as short, long, etc., nor do they require us to detect the endianness of the machine we are running on. It is possible they should be macros for speed, but I'm not sure it is worth bothering. We don't have signed versions although we could add them if needed. They are, of course little-endian as that is the byteorder used by all integers in a BACKUP saveset. */ unsigned long getu32 (addr) unsigned char *addr; { return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) | addr[1]) << 8 | addr[0]; } unsigned int getu16 (addr) unsigned char *addr; { return (addr[1] << 8) | addr[0]; } struct bbh { unsigned char bbh_dol_w_size[2]; unsigned char bbh_dol_w_opsys[2]; unsigned char bbh_dol_w_subsys[2]; unsigned char bbh_dol_w_applic[2]; unsigned char bbh_dol_l_number[4]; char bbh_dol_t_spare_1[20]; unsigned char bbh_dol_w_struclev[2]; unsigned char bbh_dol_w_volnum[2]; unsigned char bbh_dol_l_crc[4]; unsigned char bbh_dol_l_blocksize[4]; unsigned char bbh_dol_l_flags[4]; char bbh_dol_t_ssname[32]; unsigned char bbh_dol_w_fid[3][2]; unsigned char bbh_dol_w_did[3][2]; char bbh_dol_t_filename[128]; char bbh_dol_b_rtype; char bbh_dol_b_rattrib; unsigned char bbh_dol_w_rsize[2]; char bbh_dol_b_bktsize; char bbh_dol_b_vfcsize; unsigned char bbh_dol_w_maxrec[2]; unsigned char bbh_dol_l_filesize[4]; char bbh_dol_t_spare_2[22]; unsigned char bbh_dol_w_checksum[2]; } *block_header; struct brh { unsigned char brh_dol_w_rsize[2]; unsigned char brh_dol_w_rtype[2]; unsigned char brh_dol_l_flags[4]; unsigned char brh_dol_l_address[4]; unsigned char brh_dol_l_spare[4]; } *record_header; /* define record types */ #define brh_dol_k_null 0 #define brh_dol_k_summary 1 #define brh_dol_k_volume 2 #define brh_dol_k_file 3 #define brh_dol_k_vbn 4 #define brh_dol_k_physvol 5 #define brh_dol_k_lbn 6 #define brh_dol_k_fid 7 struct bsa { unsigned char bsa_dol_w_size[2]; unsigned char bsa_dol_w_type[2]; char bsa_dol_t_text[1]; } *data_item; #ifdef STREAM char *def_tapefile = "/dev/rts8"; #else char *def_tapefile = "/dev/rmt8"; #endif char filename[128]; int filesize; char recfmt; /* record format */ #define FAB_dol_C_UDF 0 /* undefined */ #define FAB_dol_C_FIX 1 /* fixed-length record */ #define FAB_dol_C_VAR 2 /* variable-length record */ #define FAB_dol_C_VFC 3 /* variable-length with fixed-length control record */ #define FAB_dol_C_STM 4 /* RMS-11 stream record (valid only for sequential org) */ #define FAB_dol_C_STMLF 5 /* stream record delimited by LF (sequential org only) */ #define FAB_dol_C_STMCR 6 /* stream record delimited by CR (sequential org only) */ #define FAB_dol_C_MAXRFM 6 /* maximum rfm supported */ char recatt; /* record attributes */ #define FAB_dol_V_FTN 0 /* FORTRAN carriage control character */ #define FAB_dol_V_CR 1 /* line feed - record -carriage return */ #define FAB_dol_V_PRN 2 /* print-file carriage control */ #define FAB_dol_V_BLK 3 /* records don't cross block boundaries */ FILE *f = NULL; /* Number of bytes we have read from the current file so far (or something like that; see process_vbn). */ int file_count; short reclen; short fix; short recsize; int vfcsize; /* Number of files we have seen. */ unsigned int nfiles; /* Number of blocks in those files. */ unsigned long nblocks; #ifdef NEWD FILE *lf; #endif int fd; /* tape file descriptor */ /* Command line stuff. */ /* The save set that we are listing or extracting. */ char *tapefile; /* We're going to say tflag indicates our best effort at the same output format as VMS BACKUP/LIST. Previous versions of this program used tflag for a briefer output format; if we want that feature we'll have to figure out what option should control it. */ int tflag; int cflag, dflag, eflag, sflag, vflag, wflag, xflag; /* Extract files in binary mode. FIXME: Haven't tried to document (here or in the manpage) exactly what that means. I think it probably means (or should mean) to give us raw bits, the same as you would get if you read the file in VMS directly using SYS$QIO or some such. But I haven't carefully looked at whether that is what it currently does. */ int flag_binary; /* More full listing (/FULL). */ int flag_full; /* Which save set are we reading? */ int selset; /* These variables describe the files we will be operating on. GARGV is a vector of GARGC elements, and the elements from GOPTIND to the end are the names. */ char **gargv; int goptind, gargc; int setnr; #define LABEL_SIZE 80 char label[LABEL_SIZE]; char *block; /* Default blocksize, as specified in -b option. */ int blocksize = 32256; #if HAVE_MT_IOCTLS struct mtop op; #endif int typecmp (); FILE * openfile(fn) char *fn; { char ufn[256]; char ans[80]; char *p, *q, s, *ext; int procf; procf = 1; /* copy fn to ufn and convert to lower case */ p = fn; q = ufn; while (*p) { if (isupper(*p)) *q = *p - 'A' + 'a'; else *q = *p; p++; q++; } *q = '\0'; /* convert the VMS to UNIX and make the directory path */ p = ufn; q = ++p; while (*q) { if (*q == '.' || *q == ']') { s = *q; *q = '\0'; if(procf && dflag) mkdir(p, 0777); *q = '/'; if (s == ']') break; } *q++; } *q++; if(!dflag) p=q; /* strip off the version number */ while (*q && *q != ';') { if( *q == '.') ext = q; q++; } if (cflag) { *q = ':'; } else { *q = '\0'; } if(!eflag && procf) procf = typecmp(++ext); if(procf && wflag) { printf("extract %s [ny]",filename); fflush(stdout); gets(ans); if(*ans != 'y') procf = 0; } if(procf) /* open the file for writing */ return(fopen(p, "w")); else return(NULL); } int typecmp(str) /* Compare the filename type in str with our list of file type to be ignored. Return 0 if the file is to be ignored, return 1 if the file is not in our list and should not be ignored. */ register char *str; { static char *type[] = { "exe", /* vms executable image */ "lib", /* vms object library */ "obj", /* rsx object file */ "odl", /* rsx overlay description file */ "olb", /* rsx object library */ "pmd", /* rsx post mortem dump */ "stb", /* rsx symbol table */ "sys", /* rsx bootable system image */ "tsk", /* rsx executable image */ "dir", /* directory */ "upd", "tlo", "tlb", /* text library */ "hlb", /* help library */ "" /* null string terminates list */ }; register int i; i = -1; while (*type[++i]) if (strncmp(str, type[i],3) == 0) return(0); /* found a match, file to be ignored */ return(1); /* no match found */ } void process_summary (buffer, rsize) unsigned char *buffer; size_t rsize; { size_t c; size_t dsize; char *text; int type; /* These are the various fields from the summary. */ unsigned long id = 0; char *saveset = ""; size_t saveset_size = 0; char *command = ""; size_t command_size = 0; char *written_by = ""; size_t written_by_size = 0; unsigned short grp = 0377; unsigned short usr = 0377; char *os = ""; /* '\0' terminated. */ char *osversion = "unknown"; size_t osversion_size = 7; char *nodename = ""; size_t nodename_size = 0; char *written_on = ""; size_t written_on_size = 0; char *backup_version = "unknown"; size_t backup_version_size = 7; char date[23] = ""; short date_length = 0; unsigned long blksz = 0; unsigned int grpsz = 0; unsigned int bufcnt = 0; if (!tflag) return; /* check the header word */ if (buffer[0] != 1 || buffer[1] != 1) { printf ("Cannot print summary; invalid data header\n"); return; } c = 2; while (c < rsize) { dsize = getu16 (((struct bsa *)&buffer[c])->bsa_dol_w_size); type = getu16 (((struct bsa *)&buffer[c])->bsa_dol_w_type); text = ((struct bsa *)&buffer[c])->bsa_dol_t_text; /* Probably should define constants for the cases in this switch, but I don't know anything about whether they have "official" names we should be using or anything like that. */ switch (type) { case 0: /* This seems to be used for padding at the end of the summary. */ goto end_of_summary; case 1: saveset = text; saveset_size = dsize; break; case 2: command = text; command_size = dsize; break; case 4: written_by = text; written_by_size = dsize; break; case 5: if (dsize == 4) { usr = getu16 (text); grp = getu16 (text + 2); } break; case 6: if (dsize != 8 || !(time_vms_to_asc (&date_length, date, text) & 1)) { strcpy (date, "error converting date"); date_length = strlen (date); } break; case 7: if (dsize == 2) { unsigned short oscode; oscode = getu16 (text); if (oscode == 0x800) { os = "OpenVMS AXP"; } else if (oscode == 0x400) { os = "OpenVMS VAX"; } } break; case 8: osversion = text; osversion_size = dsize; break; case 9: nodename = text; nodename_size = dsize; break; case 10: if (dsize >= 4) { id = getu32 (text); } break; case 11: written_on = text; written_on_size = dsize; break; case 12: backup_version = text; backup_version_size = dsize; break; case 13: if (dsize >= 4) { blksz = getu32 (text); } break; case 14: if (dsize >= 2) { grpsz = getu16 (text); } break; case 15: if (dsize >= 2) { bufcnt = getu16 (text); } break; default: /* I guess we'll silently ignore these, for future expansion. */ break; } c += dsize + 4; } end_of_summary: printf ("Save set: %.*s\n", saveset_size, saveset); printf ("Written by: %.*s\n", written_by_size, written_by); printf ("UIC: [%06o,%06o]\n", grp, usr); printf ("Date: %.*s\n", date_length, date); printf ("Command: %.*s\n", command_size, command); printf ("Operating system: %s version %.*s\n", os, osversion_size, osversion); printf ("BACKUP version: %.*s\n", backup_version_size, backup_version); printf ("CPU ID register: %08x\n", id); printf ("Node name: %.*s\n", nodename_size, nodename); printf ("Written on: %.*s\n", written_on_size, written_on); printf ("Block size: %lu\n", blksz); if (grpsz != 0) printf ("Group size: %u\n", grpsz); printf ("Buffer count: %u\n\n", bufcnt); /* The extra \n is to provide the blank line between the summary and the list of files that follows. */ } void process_file(buffer, rsize) char *buffer; size_t rsize; { int i, n; char *p, *q; long nblk; short dsize, lnch; short dtype; char *data; char *cfname; char *sfilename; /* Various other stuff extracted from the saveset. */ unsigned short grp = 0377; unsigned short usr = 0377; /* Number of blocks which should appear in output. This doesn't seem to always be the same as nblk. */ size_t blocks; int c; int procf; /* check the header word */ if (buffer[0] != 1 || buffer[1] != 1) { printf("Snark: invalid data header\n"); exit(1); } c = 2; while (c < rsize) { dsize = getu16 (((struct bsa *) &buffer[c])->bsa_dol_w_size); dtype = getu16 (((struct bsa *)&buffer[c])->bsa_dol_w_type); data = ((struct bsa *)&buffer[c])->bsa_dol_t_text; /* Probably should define constants for the cases in this switch, but I don't know anything about whether they have "official" names we should be using or anything like that. */ switch (dtype) { case 0x2a: /* Copy the text into filename, and '\0'-terminate it. */ p = data; q = filename; for (i = 0; i < dsize && q < filename + sizeof (filename) - 1; i++) *q++ = *p++; *q = '\0'; break; case 0x2b: /* In my example, two bytes, 0x1 0x2. */ break; case 0x2c: /* In my example, 6 bytes, 0x7a 0x2 0x57 0x0 0x1 0x1. */ break; case 0x2e: /* In my example, 4 bytes, 0x00000004. Maybe the allocation. */ break; case 0x2f: if (dsize == 4) { usr = getu16 (data); grp = getu16 (data + 2); } break; case 0x34: recfmt = data[0]; recatt = data[1]; recsize = getu16 (&data[2]); /* bytes 4-7 unaccounted for. */ nblk = getu16 (&data[10]) /* Adding in the following amount is a change that I brought over from vmsbackup 3.1. The comment there said "subject to confirmation from backup expert here" but I'll put it in until someone complains. */ + (64 * 1024) * getu16 (&data[8]); lnch = getu16 (&data[12]); /* byte 14 unaccounted for */ vfcsize = data[15]; if (vfcsize == 0) vfcsize = 2; /* bytes 16-31 unaccounted for */ break; case 0x2d: /* In my example, 6 bytes. hex 2b3c 2000 0000. */ break; case 0x30: /* In my example, 2 bytes. 0x44 0xee. */ break; case 0x31: /* In my example, 2 bytes. hex 0000. */ break; case 0x32: /* In my example, 1 byte. hex 00. */ break; case 0x33: /* In my example, 4 bytes. hex 00 0000 00. */ break; case 0x4b: /* In my example, 2 bytes. Hex 01 00. */ break; case 0x50: /* In my example, 1 byte. hex 00. */ break; case 0x57: /* In my example, 1 byte. hex 00. */ break; case 0x4f: /* In my example, 4 bytes. 05 0000 00. */ break; case 0x35: /* In my example, 2 bytes. 04 00. */ break; case 0x36: case 0x37: /* In my example, 8 bytes. Presumably a date. */ break; case 0x38: /* In my example, 8 bytes. Presumably expires date, since my examples has all zeroes here and BACKUP prints "" for expires. */ break; case 0x39: /* In my example, 8 bytes. Presumably a date. */ break; case 0x47: /* In my example, 4 bytes. 01 00c6 00. */ break; case 0x48: /* In my example, 2 bytes. 88 aa. */ break; case 0x4a: /* In my example, 2 bytes. 01 00. */ break; /* then comes 0x0, offset 0x2b7. */ } c += dsize + 4; } #ifdef DEBUG printf("recfmt = %d\n", recfmt); printf("recatt = %d\n", recatt); printf("reclen = %d\n", recsize); printf("vfcsize = %d\n", vfcsize); #endif /* I believe that "512" here is a fixed constant which should not depend on the device, the saveset, or anything like that. */ filesize = (nblk-1)*512 + lnch; blocks = (filesize + 511) / 512; #ifdef DEBUG printf("nbk = %d, lnch = %d\n", nblk, lnch); printf("filesize = 0x%x\n", filesize); #endif /* open the file */ if (f != NULL) { fclose(f); file_count = 0; reclen = 0; } procf = 0; if (goptind < gargc) { if (dflag) { cfname = filename; } else { cfname = strrchr(filename, ']') + 1; } sfilename = malloc (strlen (cfname) + 5); if (sfilename == NULL) { fprintf (stderr, "out of memory\n"); exit (1); } if (cflag) { for (i = 0; i < strlen(cfname); i++) { sfilename[i] = cfname[i]; sfilename[i+1] = '\0'; } } else { for (i = 0; i < strlen(cfname) && cfname[i] != ';'; i++) { sfilename[i] = cfname[i]; sfilename[i+1] = '\0'; } } for (i = goptind; i < gargc; i++) { procf |= match (strlocase(sfilename), strlocase(gargv[i])); } free (sfilename); } else procf = 1; if (tflag && procf && !flag_full) printf ("%-52s %8d \n", filename, blocks); if (tflag && procf && flag_full) { printf ("%s\n", filename); printf (" Size: %d\n", blocks); printf (" Owner: [%06o,%06o]\n", grp, usr); printf (" Record format: "); switch (recfmt) { case FAB_dol_C_UDF: printf ("(undefined)"); break; case FAB_dol_C_FIX: printf ("(fixed)"); break; case FAB_dol_C_VAR: printf ("Variable length"); #if 0 /* This might be bytes 16 and 17 of the 0x34. But I'm not completely sure where it is stored. */ if (?) printf (", maximum %u bytes", ?); #endif break; case FAB_dol_C_VFC: printf ("(VFC)"); break; case FAB_dol_C_STM: printf ("Stream"); break; case FAB_dol_C_STMLF: printf ("Stream_LF"); break; case FAB_dol_C_STMCR: printf ("Stream_CR"); break; default: printf (""); break; } printf ("\n"); printf (" Record attributes: "); if (recatt & (1 << FAB_dol_V_FTN)) printf ("Fortran "); if (recatt & (1 << FAB_dol_V_CR)) printf ("Carriage return "); if (recatt & (1 << FAB_dol_V_PRN)) printf ("Print file "); if (recatt & (1 << FAB_dol_V_BLK)) printf ("Non-spanned"); printf ("\n"); } if (xflag && procf) { /* open file */ f = openfile(filename); if(f != NULL && vflag) printf("extracting %s\n", filename); } ++nfiles; nblocks += blocks; } /* * * process a virtual block record (file record) * */ process_vbn(buffer, rsize) char *buffer; unsigned short rsize; { int c, i; int j; if (f == NULL) { return; } i = 0; while (file_count+i < filesize && i < rsize) { switch (recfmt) { case FAB_dol_C_FIX: if (reclen == 0) { reclen = recsize; } fputc(buffer[i], f); i++; reclen--; break; case FAB_dol_C_VAR: case FAB_dol_C_VFC: if (reclen == 0) { reclen = getu16 (&buffer[i]); #ifdef NEWD fprintf(lf, "---\n"); fprintf(lf, "reclen = %d\n", reclen); fprintf(lf, "i = %d\n", i); fprintf(lf, "rsize = %d\n", rsize); #endif fix = reclen; if (flag_binary) for (j = 0; j < 2; j++) { fputc (buffer[i+j], f); } i += 2; if (recfmt == FAB_dol_C_VFC) { if (flag_binary) for (j = 0; j < vfcsize; j++) { fputc (buffer[i+j], f); } i += vfcsize; reclen -= vfcsize; } } else if (reclen == fix && recatt == (1 << FAB_dol_V_FTN)) { /**** if (buffer[i] == '0') fputc('\n', f); else if (buffer[i] == '1') fputc('\f', f); *** sow ***/ fputc(buffer[i],f); /** sow **/ i++; reclen--; } else { fputc(buffer[i], f); i++; reclen--; } if (reclen == 0) { if (!flag_binary) fputc('\n', f); if (i & 1) { if (flag_binary) fputc (buffer[i], f); i++; } } break; case FAB_dol_C_STM: case FAB_dol_C_STMLF: if (reclen < 0) { printf("SCREAM\n"); } if (reclen == 0) { reclen = 512; } c = buffer[i++]; reclen--; if (c == '\n') { reclen = 0; } fputc(c, f); break; case FAB_dol_C_STMCR: c = buffer[i++]; if (c == '\r' && !flag_binary) fputc('\n', f); else fputc(c, f); break; default: fclose(f); remove(filename); fprintf(stderr, "Invalid record format = %d\n", recfmt); return; } } file_count += i; } /* * * process a backup block * */ void process_block(block, blocksize) char *block; int blocksize; { unsigned short bhsize, rsize, rtype; unsigned long bsize, i; i = 0; /* read the backup block header */ block_header = (struct bbh *) &block[i]; i += sizeof(struct bbh); bhsize = getu16 (block_header->bbh_dol_w_size); bsize = getu32 (block_header->bbh_dol_l_blocksize); /* check the validity of the header block */ if (bhsize != sizeof(struct bbh)) { fprintf (stderr, "Invalid header block size: expected %d got %d\n", sizeof (struct bbh), bhsize); exit(1); } if (bsize != 0 && bsize != blocksize) { fprintf(stderr, "Snark: Invalid block size\n"); exit(1); } #ifdef DEBUG printf("new block: i = %d, bsize = %d\n", i, bsize); #endif /* read the records */ while (i < bsize) { /* read the backup record header */ record_header = (struct brh *) &block[i]; i += sizeof(struct brh); rtype = getu16 (record_header->brh_dol_w_rtype); rsize = getu16 (record_header->brh_dol_w_rsize); #ifdef DEBUG printf("rtype = %d\n", rtype); printf("rsize = %d\n", rsize); printf("flags = 0x%x\n", getu32 (record_header->brh_dol_l_flags)); printf("addr = 0x%x\n", getu32 (record_header->brh_dol_l_address)); printf("i = %d\n", i); #endif switch (rtype) { case brh_dol_k_null: #ifdef DEBUG printf("rtype = null\n"); #endif /* This is the type used to pad to the end of a block. */ break; case brh_dol_k_summary: #ifdef DEBUG printf("rtype = summary\n"); #endif process_summary (&block[i], rsize); break; case brh_dol_k_file: #ifdef DEBUG printf("rtype = file\n"); #endif process_file(&block[i], rsize); break; case brh_dol_k_vbn: #ifdef DEBUG printf("rtype = vbn\n"); #endif process_vbn(&block[i], rsize); break; case brh_dol_k_physvol: #ifdef DEBUG printf("rtype = physvol\n"); #endif break; case brh_dol_k_lbn: #ifdef DEBUG printf("rtype = lbn\n"); #endif break; case brh_dol_k_fid: #ifdef DEBUG printf("rtype = fid\n"); #endif break; default: /* It is quite possible that we should just skip this without even printing a warning. */ fprintf (stderr, " Warning: unrecognized record type\n"); fprintf (stderr, " record type = %d, size = %d\n", rtype, rsize); } #ifdef pyr i = i + rsize; #else i += rsize; #endif } } int rdhead() { int i, nfound; char name[80]; nfound = 1; /* read the tape label - 4 records of 80 bytes */ while ((i = read(fd, label, LABEL_SIZE)) != 0) { if (i != LABEL_SIZE) { fprintf(stderr, "Snark: bad label record\n"); exit(1); } if (strncmp(label, "VOL1",4) == 0) { sscanf(label+4, "%14s", name); if(vflag || tflag) printf("Volume: %s\n",name); } if (strncmp(label, "HDR1",4) == 0) { sscanf(label+4, "%14s", name); sscanf(label+31, "%4d", &setnr); } /* get the block size */ if (strncmp(label, "HDR2", 4) == 0) { nfound = 0; sscanf(label+5, "%5d", &blocksize); #ifdef DEBUG printf("blocksize = %d\n", blocksize); #endif } } if((vflag || tflag) && !nfound) printf("Saveset name: %s number: %d\n",name,setnr); /* get the block buffer */ block = (char *) malloc(blocksize); if (block == (char *) 0) { fprintf(stderr, "memory allocation for block failed\n"); exit(1); } return(nfound); } void rdtail() { int i; char name[80]; /* read the tape label - 4 records of 80 bytes */ while ((i = read(fd, label, LABEL_SIZE)) != 0) { if (i != LABEL_SIZE) { fprintf(stderr, "Snark: bad label record\n"); exit(1); } if (strncmp(label, "EOF1",4) == 0) { sscanf(label+4, "%14s", name); if(vflag || tflag) printf("End of saveset: %s\n\n\n",name); } } } /* Perform the actual operation. The way this works is that main () parses the arguments, sets up the global variables like cflags, and calls us. Does not return--it always calls exit (). */ void vmsbackup() { int c, i, eoffl; /* Nonzero if we are reading from a saveset on disk (as created by the /SAVE_SET qualifier to BACKUP) rather than from a tape. */ int ondisk; if (tapefile == NULL) tapefile = def_tapefile; #ifdef NEWD /* open debug file */ lf = fopen("log", "w"); if (lf == NULL) { perror("log"); exit(1); } #endif /* open the tape file */ fd = open(tapefile, O_RDONLY); if (fd < 0) { perror(tapefile); exit(1); } #if HAVE_MT_IOCTLS /* rewind the tape */ op.mt_op = MTREW; op.mt_count = 1; i = ioctl(fd, MTIOCTOP, &op); if (i < 0) { if (errno == EINVAL || errno == ENOTTY) { ondisk = 1; } else { perror(tapefile); exit(1); } } #else ondisk = 1; #endif if (ondisk) { /* process_block wants this to match the size which backup writes into the header. Should it care in the ondisk case? */ blocksize = 32256; block = malloc (blocksize); if (block == (char *) 0) { fprintf(stderr, "memory allocation for block failed\n"); exit(1); } eoffl = 0; } else { eoffl = rdhead(); } nfiles = 0; nblocks = 0; /* read the backup tape blocks until end of tape */ while (!eoffl) { if(sflag && setnr != selset) { if (ondisk) { fprintf(stderr, "-s not supported for disk savesets\n"); exit(1); } #if HAVE_MT_IOCTLS op.mt_op = MTFSF; op.mt_count = 1; i = ioctl(fd, MTIOCTOP, &op); if (i < 0) { perror(tapefile); exit(1); } #else abort (); #endif i = 0; } else i = read(fd, block, blocksize); if(i == 0) { if (ondisk) { /* No need to support multiple save sets. */ eoffl = 1; } else { if (vflag || tflag) printf ("\n\ Total of %u files, %lu blocks\n", nfiles, nblocks); rdtail(); eoffl=rdhead(); } } else if (i == -1) { perror ("error reading saveset"); exit (1); } else if (i != blocksize) { fprintf(stderr, "bad block read i = %d\n", i); exit(1); } else{ eoffl = 0; process_block(block, blocksize); } } if(vflag || tflag) { if (ondisk) { printf ("\nTotal of %u files, %lu blocks\n", nfiles, nblocks); printf("End of save set\n"); } else { printf("End of tape\n"); } } /* close the tape */ close(fd); #ifdef NEWD /* close debug file */ fclose(lf); #endif /* exit cleanly */ exit(0); } vmsbackup-dist/match.c100644 765 144 15555 6272166216 14353 0ustar kingdonusers#include #include #define ASTERISK '*' /* The '*' metacharacter */ #define QUESTION '?' /* The '?' metacharacter */ #define LEFT_BRACKET '[' /* The '[' metacharacter */ #define RIGHT_BRACKET ']' /* The ']' metacharacter */ #define IS_OCTAL(ch) (ch >= '0' && ch <= '7') /* Filenames in BACKUP savesets can contain only these uppercase letters, I think. */ #define IS_UC(ch) (ch >= 'A' && ch <= 'Z') typedef int BOOLEAN; #define VOID void #define TRUE 1 #define FALSE 0 #define EOS '\000' static BOOLEAN do_list (); static char nextch (); static VOID list_parse (); /* * FUNCTION * * match test string for wildcard match * * SYNOPSIS * * BOOLEAN match (string, pattern) * register char *string; * register char *pattern; * * DESCRIPTION * * Test string for match using pattern. The pattern may * contain the normal shell metacharacters for pattern * matching. The '*' character matches any string, * including the null string. The '?' character matches * any single character. A list of characters enclosed * in '[' and ']' matches any character in the list. * If the first character following the beginning '[' * is a '!' then any character not in the list is matched. * */ /* * PSEUDO CODE * * Begin match * Switch on type of pattern character * Case ASTERISK: * Attempt to match asterisk * Break * Case QUESTION MARK: * Attempt to match question mark * Break * Case EOS: * Match is result of EOS on string test * Break * Case default: * If explicit match then * Match is result of submatch * Else * Match is FALSE * End if * Break * End switch * Return result of match test * End match * */ BOOLEAN match (string, pattern) register char *string; register char *pattern; { register BOOLEAN ismatch; ismatch = FALSE; switch (*pattern) { case ASTERISK: pattern++; do { ismatch = match (string, pattern); } while (!ismatch && *string++ != EOS); break; case QUESTION: if (*string != EOS) { ismatch = match (++string, ++pattern); } break; case EOS: if (*string == EOS) { ismatch = TRUE; } break; case LEFT_BRACKET: if (*string != EOS) { ismatch = do_list (string, pattern); } break; default: if (*string++ == *pattern++) { ismatch = match (string, pattern); } else { ismatch = FALSE; } break; } return (ismatch); } /* * FUNCTION * * do_list process a list and following substring * * SYNOPSIS * * static BOOLEAN do_list (string, pattern) * register char *string; * register char *pattern; * * DESCRIPTION * * Called when a list is found in the pattern. Returns * TRUE if the current character matches the list and * the remaining substring matches the remaining pattern. * * Returns FALSE if either the current character fails to * match the list or the list matches but the remaining * substring and subpattern's don't. * * RESTRICTIONS * * The mechanism used to match characters in an inclusive * pair (I.E. [a-d]) may not be portable to machines * in which the native character set is not ASCII. * * The rules implemented here are: * * (1) The backslash character may be * used to quote any special character. * I.E. "\]" and "\-" anywhere in list, * or "\!" at start of list. * * (2) The sequence \nnn becomes the character * given by nnn (in octal). * * (3) Any non-escaped ']' marks the end of list. * * (4) A list beginning with the special character * '!' matches any character NOT in list. * The '!' character is only special if it * is the first character in the list. * */ /* * PSEUDO CODE * * Begin do_list * Default result is no match * Skip over the opening left bracket * If the next pattern character is a '!' then * List match gives FALSE * Skip over the '!' character * Else * List match gives TRUE * End if * While not at closing bracket or EOS * Get lower and upper bounds * If character in bounds then * Result is same as sense flag. * Skip over rest of list * End if * End while * If match found then * If not at end of pattern then * Call match with rest of pattern * End if * End if * Return match result * End do_list * */ static BOOLEAN do_list (string, pattern) register char *string; char *pattern; { register BOOLEAN ismatch; register BOOLEAN if_found; register BOOLEAN if_not_found; auto char lower; auto char upper; pattern++; if (*pattern == '!') { if_found = FALSE; if_not_found = TRUE; pattern++; } else { if_found = TRUE; if_not_found = FALSE; } ismatch = if_not_found; while (*pattern != ']' && *pattern != EOS) { list_parse (&pattern, &lower, &upper); if (*string >= lower && *string <= upper) { ismatch = if_found; while (*pattern != ']' && *pattern != EOS) {pattern++;} } } if (*pattern++ != ']') { fprintf (stderr, "warning - character class error\n"); } else { if (ismatch) { ismatch = match (++string, pattern); } } return (ismatch); } /* * FUNCTION * * list_parse parse part of list into lower and upper bounds * * SYNOPSIS * * static VOID list_parse (patp, lowp, highp) * char **patp; * char *lowp; * char *highp; * * DESCRIPTION * * Given pointer to a pattern pointer (patp), pointer to * a place to store lower bound (lowp), and pointer to a * place to store upper bound (highp), parses part of * the list, updating the pattern pointer in the process. * * For list characters which are not part of a range, * the lower and upper bounds are set to that character. * */ static VOID list_parse (patp, lowp, highp) char **patp; char *lowp; char *highp; { *lowp = nextch (patp); if (**patp == '-') { (*patp)++; *highp = nextch (patp); } else { *highp = *lowp; } } /* * FUNCTION * * nextch determine next character in a pattern * * SYNOPSIS * * static char nextch (patp) * char **patp; * * DESCRIPTION * * Given pointer to a pointer to a pattern, uses the pattern * pointer to determine the next character in the pattern, * subject to translation of backslash-char and backslash-octal * sequences. * * The character pointer is updated to point at the next pattern * character to be processed. * */ static char nextch (patp) char **patp; { register char ch; register char chsum; register int count; ch = *(*patp)++; if (ch == '\\') { ch = *(*patp)++; if (IS_OCTAL (ch)) { chsum = 0; for (count = 0; count < 3 && IS_OCTAL (ch); count++) { chsum *= 8; chsum += ch - '0'; ch = *(*patp)++; } (*patp)--; ch = chsum; } } return (ch); } char * strlocase(str) char *str; { int i; for (i = 0; i < strlen(str); i++) { if (IS_UC (str[i])) { str[i] = str[i] - 'A' + 'a'; } } return (str); } vmsbackup-dist/NEWS100644 765 144 2122 6272200452 13543 0ustar kingdonusersChanges since version 3.0.1: * Merged in changes from version 3.1 from S.J. Tappin of the University of Birmingham. .HLB is now on the list of file extensions which are ignored by default (along with .TLB, .EXE, etc.). Filenames are matched in a case-insensitive fashion. Added -B option to extract files in binary mode. * Merged in the -b change from Timothy Stark's version 4.0. This lets one set the blocksize. Changes from version 3.0 to version 3.0.1: * The output from listings now more closely approximates the output of VMS BACKUP/LIST. * There is now a version which runs on VMS and accepts (a few) of the qualifiers of BACKUP. * Can read savesets on disk (created with the /SAVE_SET qualifier) as well as on tape. * More portable (in particular, no longer depends on things like #ifdef sun to determined byte-order, and no longer depends on the size of host data types like short and long). Changes which were in version 3.0: (Version 3.0 is also known as "v07i099: Read VMS backup tapes" (read-vms-backs) from the comp.sources.unix archive) * See the bottom of the ChangeLog file. vmsbackup-dist/build.com100644 765 144 501 6272201537 14627 0ustar kingdonusers$ CC VMSBACKUP.C/DEFINE=(HAVE_MT_IOCTLS=0,HAVE_UNIXIO_H=1) $ CC DCLMAIN.C $! Probably we don't want match as it probably doesn't implement VMS-style $! matching, but I haven't looking into the issues yet. $ CC match $ LINK/exe=VMSBACKUP.EXE vmsbackup.obj,dclmain.obj,match.obj,sys$input/opt identification="VMSBACKUP4.1" vmsbackup-dist/dclmain.c100644 765 144 7627 6250172135 14640 0ustar kingdonusers/* This is the version of main() for DCL-style option parsing. */ /* FIXME: Probably should be using init_cli from the VERB distribution so that we can be defined as a foreign command as well as by SET COMMAND. */ #include #include #include #include #include "vmsbackup.h" #include "sysdep.h" /* FIXME: Is there a header file for any of the following? */ extern int cli$get_value(); extern int cli$present(); extern int sys$asctim (); int main (argc, argv) int argc; char *argv[]; { static $DESCRIPTOR (file1,"P1"); static $DESCRIPTOR (file2,"P2"); /* Is the /SAVE_SET qualifier specified with P1 and P2, respectively. */ int p1_saveset = 0; int p2_saveset = 0; /* Was P2 specified? */ int p2_specified; static $DESCRIPTOR (q_saveset, "SAVE_SET"); static $DESCRIPTOR (q_list, "LIST"); static $DESCRIPTOR (q_full, "FULL"); static $DESCRIPTOR (q_brief, "BRIEF"); static struct dsc$descriptor result = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0}; /* We don't yet have a mechanism to select only some files from a save set. */ gargv = NULL; gargc = 0; goptind = 0; cflag=dflag=eflag=sflag=tflag=vflag=wflag=xflag=0; flag_full = 0; if (cli$get_value (&file1, &result) & 1) { if (cli$present (&q_saveset) & 1) { p1_saveset = 1; } tapefile = malloc (result.dsc$w_length + 1); if (tapefile == NULL) { fprintf (stderr, "out of memory!\n"); exit (EXIT_FAILURE); } strncpy (tapefile, result.dsc$a_pointer, result.dsc$w_length); tapefile[result.dsc$w_length] = '\0'; } if (cli$get_value (&file2, &result) & 1) { if (cli$present (&q_saveset) & 1) { p2_saveset = 1; } if (result.dsc$w_length != 2 || result.dsc$a_pointer[0] != '[' || result.dsc$a_pointer[1] != ']') { fprintf (stderr, "error: extraction only supported to []\n"); exit (EXIT_FAILURE); } p2_specified = 1; } if (cli$present (&q_list) & 1) { tflag = 1; } if (cli$present (&q_full) & 1) { flag_full = 1; } if (cli$present (&q_brief) & 1) { /* /BRIEF overrides /FULL. */ flag_full = 0; } /* Note that our processing of the parameters, /SAVE_SET, etc. differs somewhat from BACKUP. In some cases this perhaps should be changed, but in other cases I'm not sure the way BACKUP does it is very good. For example, did the user who typed "BACKUP [-]TESTB.BCK []" really mean to copy the saveset intact? Seems to me that an error is more useful in that case. */ /* If P1 or P2 refers to a tape device, we should be setting p1_saveset or p2_saveset. But this is not yet implemented (FIXME). */ if (!p1_saveset) { fprintf (stderr, "error: must specify /SAVE_SET\n"); exit (EXIT_FAILURE); } if (p2_saveset) { fprintf (stderr, "error: creating savesets not implemented\n"); exit (EXIT_FAILURE); } xflag = !tflag; if (tflag && p2_specified) { fprintf (stderr, "error: must supply two parameters when extracting\n"); exit (EXIT_FAILURE); } if (xflag && !p2_specified) { fprintf (stderr, "error: must supply one parameter with /LIST\n"); exit (EXIT_FAILURE); } vmsbackup (); } /* The following is code for VMS systems which isn't related to main() or option parsing. It should perhaps be part of a separate file (depending, of course, on things like whether anyone ever feels like separating the two concepts). */ /* Given an 8-byte VMS-format date (little-endian) in SRCTIME, put an ASCII representation in *ASCBUFFER and put the length in *ASCLENGTH. ASCBUFFER must be big enough for 23 characters. Returns: condition code. */ int time_vms_to_asc (asclength, ascbuffer, srctime) short *asclength; char *ascbuffer; void *srctime; { struct dsc$descriptor buffer; buffer.dsc$w_length = 23; buffer.dsc$b_dtype = DSC$K_DTYPE_T; buffer.dsc$b_class = DSC$K_CLASS_S; buffer.dsc$a_pointer = ascbuffer; return sys$asctim (asclength, &buffer, srctime, 0); } vmsbackup-dist/getoptmain.c100644 765 144 5227 6272201541 15370 0ustar kingdonusers/* This is the version of main() for POSIX.2 (getopt) style arguments. */ char *version = "VMSBACKUP4.1"; #include #include #include "vmsbackup.h" #include "getopt.h" void usage (progname) char *progname; { fprintf (stderr, "\ Usage: %s -{tx}[cdevwF][-b blocksize][-s setnumber][-f tapefile]\n", progname); } extern int optind; extern char *optarg; int main (argc, argv) int argc; char *argv[]; { char *progname; int c; progname = argv[0]; if(argc < 2){ usage(progname); exit(1); } gargv = argv; gargc = argc; cflag=dflag=eflag=sflag=tflag=vflag=wflag=xflag=0; flag_binary = 0; flag_full = 0; tapefile = NULL; while((c=getopt(argc,argv,"b:cdef:s:tvwxFVB")) != EOF) switch(c){ case 'b': sscanf (optarg, "%d", &blocksize); break; case 'c': cflag++; break; case 'd': dflag++; break; case 'e': eflag++; break; case 'f': tapefile = optarg; break; case 's': sflag++; sscanf(optarg,"%d",&selset); break; case 't': tflag++; break; case 'v': vflag++; break; case 'w': wflag++; break; case 'x': xflag++; break; case 'F': /* I'd actually rather have this be --full, but at the moment I don't feel like worrying about infrastructure for parsing long arguments. I don't like the GNU getopt_long--the interface is noreentrant and generally silly; and it might be nice to have something which synergizes more closely with the VMS options (that one is a bit of a can of worms, perhaps, though) like parseargs or whatever it is called. */ flag_full = 1; break; case 'V': printf ("VMSBACKUP version %s\n", version); exit (EXIT_FAILURE); break; case 'B': /* This of course should be --binary; see above about long options. */ flag_binary = 1; break; case '?': usage(progname); exit(1); break; }; goptind = optind; if(!tflag && !xflag) { usage(progname); exit(1); } vmsbackup (); } /* The following is code for non-VMS systems which isn't related to main() or option parsing. It should perhaps be part of a separate file (depending, of course, on things like whether anyone ever feels like separating the two concepts). */ /* Given an 8-byte VMS-format date (little-endian) in SRCTIME, put an ASCII representation in *ASCBUFFER and put the length in *ASCLENGTH. ASCBUFFER must be big enough for 23 characters. Returns: condition code. */ int time_vms_to_asc (asclength, ascbuffer, srctime) short *asclength; char *ascbuffer; void *srctime; { /* Not currently implemented, although it would be nice to. */ *asclength = 0; return 1; } vmsbackup-dist/vmsbackup.cld100644 765 144 723 6223346443 15517 0ustar kingdonusers! VMSBACKUP.CLD - command definition for VMSBACKUP ! ! Eventually the verb here will be just BACKUP, but since this utility ! is not full-featured (yet) and we want to conveniently use both it ! and BACKUP during testing, we call it VMSBACKUP for now. DEFINE VERB VMSBACKUP IMAGE VMSBACKUP:VMSBACKUP.EXE PARAMETER P1 PARAMETER P2 QUALIFIER SAVE_SET, PLACEMENT=local QUALIFIER LIST, PLACEMENT=global QUALIFIER FULL, PLACEMENT=global QUALIFIER BRIEF, PLACEMENT=global vmsbackup-dist/vmsbackup.h100644 765 144 567 6272174501 15210 0ustar kingdonusers/* Variables and functions exported from vmsbackup.c. See vmsbackup.c for comments on each variable or function. */ extern int cflag, dflag, eflag, sflag, tflag, vflag, wflag, xflag; extern int flag_binary; extern int flag_full; extern char *tapefile; extern int selset; extern int blocksize; extern void vmsbackup (); extern char **gargv; extern int goptind, gargc; vmsbackup-dist/sysdep.h100750 765 144 151 6250172135 14504 0ustar kingdonusers/* Variables and functions exported from dclmain.c and getoptmain.c. */ extern int time_vms_to_asc ();