1 /*% cc -O -o getvms %
2  *
3  *  Copy files from VMS (ODS-2) filesystem.  Files may be copied to
4  *  disk or to standard output.  Transfer modes supported are "text"
5  *  (RMS stuff is thrown away, newline is tacked on the end of each
6  *  VMS record), and "image" (straight byte-by-byte transfer).	There
7  *  were once plans to support a third mode ("binary"), but this has
8  *  not yet been implemented.  Defaults for the output destination and
9  *  transfer mode are set by #defines, but the destination/mode can be
10  *  specified at runtime by using various flags (see "options()").
11  *
12  *  The input device and directory, if omitted, will default to that of
13  *  the previous filespec.  Note that this means that the first filespec
14  *  MUST have a directory specified, and (if DFLTDEV is not defined) also
15  *  a device as well.  The filename syntax is the same as the standard
16  *  VMS naming scheme, except that a "." may be used to separate the
17  *  filetype from the version number, and some delimiters may be changed
18  *  via #defines, if desired.  (This is all to avoid the possibility of
19  *  having to escape some of the characters that the shell treats as
20  *  special.)  The device name is the name of the UNIX special file in
21  *  /dev, rather than what VMS thinks it would be.
22  *
23  *  If the first character of argv[0] is "l", or if the "-l" option is
24  *  used, the program lists the contents of the directory rather than
25  *  copying a file.  At present, only one directory may be listed per
26  *  command.
27  *
28  *  Written by Mark Bartelt, based on an earlier program which copied
29  *  files from ODS-1 volumes.
30  *
31  *	hacked 6-aug-82 norman wilson:
32  *		in getvb, don't let h_use get sign extended
33  *		added -T & line number stripping
34  *		bwk'd putch a bit in the process
35  *		added access checks to openout to plug a security hole
36  *		this last should probably be commented out for non-unix
37  *
38  *	hacked 16-aug-84  Sam Sjogren:
39  *		In gethdr(), check to see if the user is the
40  *		superuser.  If so, ignore file protection.
41  *
42  *	hacked 26-feb-85  Carl J Lydick
43  *		In getde(), check for end-of-file occurring as first word
44  *		in the block returned by getvb().  Failure to do so causes
45  *		garbage and core dumps.
46  *      hacked 22-feb-93  George J Carrette (GJC@MITECH.COM)
47  *              Made this run under VMS, just for the heck of it.
48  *              Updated crack() to allow newer VMS filename syntax.
49  *      hacked 1-jun-93 (GJC@MITECH.COM) to have procedure prototypes,
50  *              and use stdarg.h so that DEC C ANSI89 will be happy
51  *              and member_alignment pragma on alpha, just in case.
52  */
53 
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <stdarg.h>
57 #include <string.h>
58 #include <fcntl.h>
59 #include <unistd.h>
60 
61 #ifdef VMS
62 #ifdef __ALPHA
63 /* define these so people don't need to compile with
64    the flag /PREFIX_LIBRARY_ENTRIES=ALL_ENTRIES because
65    /STANDARD=RELAXED_ANSI89 is the default
66 */
67 #define access decc$access
68 #define getgid decc$getgid
69 #define getuid decc$getuid
70 #endif
71 #define index strchr
72 #define rindex strrchr
73 #include <string.h>
74 #include <descrip.h>
75 #include <ssdef.h>
76 #include <iodef.h>
77 #include <starlet.h>
78 #include <unixlib.h>
79 #include <unixio.h>
80 #endif
81 
82 #define DFLTDEV "disk$users"
83 
84 #define err0(msg)	{ errmsg(msg,0); return(0); }
85 #define err1(msg,arg)	{ errmsg(msg,arg); return(0); }
86 
87 #define alphnum(x)	( ( 'a'<=(x) && (x)<='z' ) || ( 'A'<=(x) && (x)<='Z' ) || ( '0'<=(x) && (x)<='9' ) || ((x) == '-') || ((x) == '_') || ((x) == '$') )
88 
89 #define decimal(x)	( '0'<=(x) && (x)<='9' )
90 
91 typedef unsigned short	ushort;
92 
93 #ifdef __ALPHA
94 #pragma member_alignment __save
95 #pragma nomember_alignment
96 #endif
97 
98 struct filnam {
99 	char	f_nam[14];	/* File name (ASCII) */
100 	ushort	f_ver;		/* Version number */
101 };
102 
103 struct uic {
104 	ushort	u_prog; 	/* Programmer number */
105 	ushort	u_proj; 	/* Project number */
106 };
107 
108 struct fileid {
109 	ushort	f_num;		/* File number */
110 	ushort	f_seq;		/* File sequence number (worthless concept) */
111 	ushort	f_rvn;		/* Relative volume number (ditto and MBZ) */
112 };
113 
114 struct rms {
115 	char	f_forg; 	/* Record format and file organization */
116 	char	f_ratt; 	/* Record attributes */
117 	ushort	f_rsiz; 	/* Record size */
118 	ushort	f_hvbn[2];	/* Highest VBN allocated */
119 	ushort	f_heof[2];	/* End of file block */
120 	ushort	f_ffby; 	/* First free byte */
121 	char	f_bksz; 	/* Bucket size */
122 	char	f_hdsz; 	/* Fixed header size */
123 	ushort	f_mrs;		/* Maximum record size */
124 	ushort	f_deq;		/* Default extend quantity */
125 };
126 
127 struct ident {
128 	char	i_fnam[20];	/* File name */
129 	ushort	i_rvno; 	/* Revision number */
130 	char	i_crdt[8];	/* Creation date and time */
131 	char	i_rvdt[8];	/* Revision date and time */
132 	char	i_exdt[8];	/* Expiration date and time */
133 	char	i_bkdt[8];	/* Backup date and time */
134 	char	i_ulab[80];	/* User label */
135 };
136 
137 struct header {
138 	char	h_idof; 	/* Ident area offset */
139 	char	h_mpof; 	/* Map area offset */
140 	char	h_acof; 	/* Access control list offset */
141 	char	h_rsof; 	/* Reserved area offset */
142 	ushort	h_fseg; 	/* Extension segment number */
143 	ushort	h_flev; 	/* Structure level and version */
144 	ushort	h_fnum; 	/* File number */
145 	ushort	h_fseq; 	/* File sequence number */
146 	ushort	h_frvn; 	/* Relative volume number */
147 	ushort	h_efnu; 	/* Extension file number */
148 	ushort	h_efsq; 	/* Extension file sequence number */
149 	ushort	h_ervn; 	/* Extension relative volume number */
150 	union {
151 		char	hu_ufat[32];	/* User file attributes */
152 		struct rms hu_rms;	/* RMS file attributes */
153 	} h_ufat;
154 #define h_rms	h_ufat.hu_rms
155 	char	h_fcha[4];	/* File characteristics */
156 #define h_ucha	h_fcha[0]	/* User controlled characteristics */
157 #define h_scha	h_fcha[1]	/* System controlled characteristics */
158 	char	h_UU1[2];	/* Unused 1 */
159 	char	h_use;		/* Map words in use */
160 	char	h_priv; 	/* Accessor privilege level */
161 	struct uic h_fown;	/* File owner UIC */
162 #define h_prog	h_fown.u_prog	/* Programmer (member) number */
163 #define h_proj	h_fown.u_proj	/* Project (group) number */
164 	ushort	h_fpro; 	/* File protection code */
165 	ushort	h_rpro; 	/* Record protection code */
166 	char	h_UU2[4];	/* Ununsed 2 */
167 	char	h_semk[4];	/* Security mask */
168 	struct ident h_ident;	/* Ident area */
169 	char	h_other[300];	/* Map area, access control area, etc */
170 };
171 
172 struct homeblock {
173 	long	H_hblb; 	/* Home block LBN */
174 	long	H_ahlb; 	/* Alternate home block LBN */
175 	long	H_ihlb; 	/* Backup index file header LBN */
176 	char	H_vlev[2];	/* Structure level and version */
177 	ushort	H_sbcl; 	/* Storage bitmap cluster factor */
178 	ushort	H_hbvb; 	/* Home block VBN */
179 	ushort	H_ahvb; 	/* Backup home block VBN */
180 	ushort	H_ihvb; 	/* Backup index file header VBN */
181 	ushort	H_ibvb; 	/* Index file bitmap VBN */
182 	ushort	H_iblb[2];	/* Index file bitmap LBN */
183 	long	H_fmax; 	/* Maximum number of files */
184 	ushort	H_ibsz; 	/* Index file bitmap size */
185 	ushort	H_rsvf; 	/* Number of reserved files */
186 	ushort	H_dvty; 	/* Disk device type */
187 	ushort	H_rvn;		/* Relative volume number */
188 	ushort	H_nvol; 	/* Number of volumes */
189 	ushort	H_vcha; 	/* Volume characteristics */
190 	struct uic H_vown;	/* Volume owner UIC */
191 	long	H_vsmx; 	/* Volume security mask */
192 	ushort	H_vpro; 	/* Volume protection code */
193 	ushort	H_dfpr; 	/* Default file protection */
194 	ushort	H_drpr; 	/* Default record protection */
195 	ushort	H_chk1; 	/* First checksum */
196 	char	H_vdat[8];	/* Volume creation date */
197 	char	H_wisz; 	/* Default window size */
198 	char	H_lruc; 	/* Directory pre-access limit */
199 	ushort	H_fiex; 	/* Default file extend */
200 	char	H_UU1[388];	/* Unused 1 */
201 	char	H_snam[12];	/* Structure name */
202 	char	H_indn[12];	/* Volume name */
203 	char	H_indo[12];	/* Volume owner */
204 	char	H_indf[12];	/* Format type */
205 	char	H_UU2[2];	/* Unused 2 */
206 	ushort	H_chk2; 	/* Second checksum */
207 } hblock;
208 
209 struct directory {
210 	ushort	d_rbc;		/* Record byte count */
211 	ushort	d_vrlm; 	/* Version limit */
212 	char	d_flags;	/* Flags */
213 	char	d_nbc;		/* Name byte count */
214 	char	d_fname[1];	/* File name string */
215 };
216 
217 struct dirval {
218 	ushort	d_ver;		/* Version number */
219 	struct fileid d_fid;	/* File ID */
220 };
221 
222 #ifdef __ALPHA
223 #pragma member_alignment __restore
224 #endif
225 
226 #define BUFSIZE 512
227 
228 #define bit(x)	((01)<<(x))
229 
230 #define DEV	bit(0)
231 #define DIR	bit(1)
232 #define FIL	bit(2)
233 #define EXT	bit(3)
234 #define VER	bit(4)
235 
236 #define DIRBEG	'['
237 #define DIREND	']'
238 
239 #define NULLCHR '\0'
240 #define NULLSTR ""
241 
242 #define FSMAX	100
243 #define DEVMAX	20
244 
245 #define TEXT	0
246 #define IMGRMS	1
247 #define IMGFULL 2
248 #define BINARY	3
249 
250 #define DISK	0
251 #define STDOUT	1
252 
253 #define DFLTMOD TEXT
254 #define DFLTOUT DISK
255 
256 void usage(void);
257 void options(char *arg);
258 void getvms(void);
259 void errmsg(char *msg,...);
260 int openin(void);
261 void listdir(void);
262 int copyfile(void);
263 int crack(void);
264 int openvms(char *devname);
265 int finddir(void);
266 int gethdr(ushort fnum,struct header *hp);
267 int getlb(long	lbn,char *buf);
268 void dirmsg(char *msg,char *dirname,char *ptr);
269 int okwrite(char *file);
270 int getvb(long vbn,char *buf,struct header *hp);
271 void putch(char c);
272 void prtfn(struct directory *de,struct dirval *vp);
273 int convert(char *fl,char *tp,char *vr,struct filnam *f);
274 long lbnbase(register ushort *rp);
275 int openout(void);
276 
277 char	**av;				/* Global argv */
278 char	lsflag = 0;			/* Nonzero ==> list directory */
279 int	xfermode = DFLTMOD;		/* Transfer mode */
280 int	rmlineno = 0;			/* ntw - remove sos line numbers */
281 int	outdest = DFLTOUT;		/* Output destination */
282 char	filspec[FSMAX]; 		/* Full filename string being processed */
283 int	pflags; 			/* Flags returned by crack() */
284 char	*dev, *dir, *fil, *typ, *ver;	/* Pointers to cracked filename fields */
285 char	vmsdev[DEVMAX+6];		/* Special file name for VMS filesystem */
286 int	vmsfd = -1;			/* File descriptor for reading VMS filesystem */
287 FILE	*of;				/* Stream pointer for output file */
288 char	dirfound;			/* Directory found */
289 
290 struct header	indexh, mfdh, dirh, fileh;	/* File headers for index file, MFD,
291  UFD, and file */
292 
293 
main(argc,argv)294 main(argc,argv)
295 int	argc;
296 char	**argv;
297 {
298 	char	*basename();
299 
300 	av = argv;
301 
302 	if ( --argc == 0 )
303 		usage();
304 
305 	if ( *basename(*argv) == 'l' )
306 		++lsflag;
307 
308 	while ( argc-- ) {
309 		if ( **++av == '-' )
310 			options(*argv);
311 		else
312 			getvms();
313 	}
314 }
315 
316 
usage(void)317 void usage(void)
318 {
319 	fprintf(stderr,"usage: %s [-t] [-i] [-b] [-d] [-f] [-s] vmsfile\n",*av);
320 	exit(-1);
321 }
322 
323 
324 char *
basename(s)325 basename(s)
326 register char	*s;
327 {
328 /* NOTE:  Some versions of UNIX use the name strrchr() rather than rindex() */
329 	char		*rindex();
330 	register char	*t;
331 
332 	if ( (t=rindex(s,
333 #ifdef VMS
334 		       ']'
335 #else
336 		       '/'
337 #endif
338 		       )) == NULL )
339 		return(s);
340 	else
341 		return(t+1);
342 }
343 
344 
345 /*
346  *  Process option flags
347  */
348 
options(char * arg)349 void options(char *arg)
350 {
351 	register char	*p;
352 
353 	for ( p = *av; *++p; ) {
354 
355 		switch ( *p ) {
356 
357 		case 'd':
358 		case 'f':	outdest = DISK; break;
359 		case 's':	outdest = STDOUT; break;
360 
361 		case 't':	xfermode = TEXT; break;
362 		case 'T':	xfermode = TEXT; rmlineno++; break;	/* ntw */
363 		case 'i':	xfermode = IMGRMS; break;
364 		case 'I':	xfermode = IMGFULL; break;
365 		case 'b':	xfermode = BINARY; break;
366 
367 		case 'l':	++lsflag; break;
368 		case 'c':	lsflag = 0; break;
369 
370 		default:	fprintf(stderr,"Invalid option (%c)\n",*p);
371 
372 		}
373 	}
374 }
375 
376 
377 /*
378  *  Get the next requested file from the VMS filesystem
379  */
380 
getvms(void)381 void getvms(void)
382 {
383 	if ( strlen(*av) > FSMAX )
384 	  {errmsg("Filespec too long");
385 	   return;}
386 	strcpy(filspec,*av);
387 
388 	if ( lsflag ) {
389 		if ( openin() )
390 			listdir();
391 	} else {
392 		if ( openin() && openout() ) {
393 			copyfile();
394 			if ( of != stdout )
395 				fclose(of);
396 		}
397 	}
398 }
399 
400 
401 /*
402  *  Open VMS file for input
403  */
404 
openin(void)405 int openin(void)
406 {
407 	static int	filecnt = 0;
408 	struct filnam	fn;
409 	ushort		fnum;
410 	ushort		search();
411 	int		gh;
412 
413 	++filecnt;
414 	if (crack() == 0)
415 		return (0);
416 	if ( pflags&DEV && !openvms(dev) )
417 		return(0);
418 #ifdef DFLTDEV
419 	if ( !(pflags&DEV) && filecnt==1 && !openvms(DFLTDEV) )
420 		return(0);
421 #endif
422 	if ( vmsfd < 0 )
423 		err0("No device specified");
424 	if ( pflags&(DEV|DIR) && !finddir() )
425 		return(0);
426 	if ( !dirfound )
427 		err0("No directory specified");
428 	if ( lsflag ) {
429 		if ( pflags & (FIL|EXT|VER) )
430 			err0("Invalid directory specification");
431 		return(1);
432 	}
433 	if ( !(pflags&EXT) )
434 		typ = NULLSTR;
435 	if ( !(pflags&VER) )
436 		ver = NULLSTR;
437 	if ( !convert(fil,typ,ver,&fn) )
438 		return(0);
439 	if ( !(fnum=search(&dirh,&fn)) )
440 		err0("File does not exist");
441 	if ( !(gh=gethdr(fnum,&fileh)) )
442 		err0("Can't get file header for file");
443 	if ( gh == -1 )
444 		err0("No access privilege for file");
445 	return(1);
446 }
447 
448 
449 /*
450  *  Crack the filename string -- First step in parsing it; just
451  *  locates the fields, doesn't do much real validity checking
452  */
453 
crack(void)454 int crack(void)
455 {
456 	register char	*p = filspec;
457 	register char	*q;
458 
459 	for ( pflags=0; *p; ) {
460 
461 		if ( *p == DIRBEG ) {
462 			if ( pflags & (DIR|FIL|EXT|VER) )
463 				err0("Bad filename syntax");
464 			dir = p+1;
465 			while ( *p != DIREND ) {
466 				if ( 'a' <= *p && *p <= 'z' )	/* SHOUT the directory */
467 					*p += 'A' - 'a';	/* name in UPPER CASE */
468 				if ( *p++ == NULLCHR )
469 					err0("Bad filename syntax");
470 			}
471 			*p++ = NULLCHR;
472 			pflags |= DIR;
473 			continue;
474 		}
475 
476 		for ( q=p; alphnum(*q); ++q )
477 			;
478 
479 		if ( *q == ':' ) {
480 			if ( pflags&(DEV|DIR|FIL|EXT|VER) )
481 				err0("Bad filename syntax");
482 			dev = p;
483 			pflags |= DEV;
484 			*q = NULLCHR;
485 			p = q + 1;
486 			continue;
487 		}
488 
489 		if ( *q == '.' || *q == ';' || *q == NULLCHR ) {
490 
491 			if ( !(pflags&FIL) ) {
492 				if ( p == q )
493 					err0("Filename missing");
494 				fil = p;
495 				pflags |= FIL;
496 				if ( *q == ';' ) {
497 					typ = NULLSTR;
498 					pflags |= EXT;
499 				}
500 			} else if ( !(pflags&EXT) ) {
501 				typ = p;
502 				pflags |= EXT;
503 			} else if ( !(pflags&VER) ) {
504 				ver = p;
505 				pflags |= VER;
506 			} else
507 				err0("Bad filename syntax");
508 
509 			if ( *q == NULLCHR ) {
510 				if ( !(pflags&EXT) )
511 					typ = NULLSTR;
512 				if ( !(pflags&VER) )
513 					ver = NULLSTR;
514 				break;
515 			}
516 			*q = NULLCHR;
517 			p = q + 1;
518 			continue;
519 		}
520 
521 		err0("Bad filename syntax");
522 	}
523 
524 	return(1);
525 }
526 
527 
528 /*
529  *  Open a disk containing an VMS filesystem
530  */
531 
openvms(char * devname)532 int openvms(char	*devname)
533 {
534 	long	ifhbn;
535 
536 	if ( strlen(devname) > DEVMAX )
537 		err1("Device name too long (%s)",devname);
538 #ifdef VMS
539 	strcpy(vmsdev,"");
540 #else
541 	strcpy(vmsdev,"/dev/");
542 	if ( strncmp(devname,"disk$",5) == 0 )
543 		devname += 5;
544 	if ( strncmp(devname,"vms",3) != 0 )
545 		strcat(vmsdev,"vms");
546 #endif
547 	strcat(vmsdev,devname);
548 #ifdef VMS
549 	{int retcode;
550 	 struct dsc$descriptor devd;
551 	 devd.dsc$w_length = strlen(vmsdev);
552 	 devd.dsc$a_pointer = vmsdev;
553 	 devd.dsc$b_class = DSC$K_CLASS_S;
554 	 devd.dsc$b_dtype = DSC$K_DTYPE_T;
555 	 vmsfd = 0;
556 	 retcode = sys$assign(&devd,&vmsfd,0,0);
557 	 if (retcode != SS$_NORMAL) vmsfd = -1;}
558 #else
559 	vmsfd=open(vmsdev,0);
560 #endif
561 	if ( vmsfd < 0 )
562 		err1("Can't open %s",vmsdev);
563 
564 	if ( !getlb(1L,(char *)&hblock) )
565 		err1("Can't read homeblock on %s",vmsdev);
566 
567 	ifhbn = ((long)hblock.H_iblb[1]<<16) + (long)hblock.H_iblb[0] + hblock.H_ibsz;
568 	if ( !getlb(ifhbn,(char *)&indexh) )
569 		err1("Can't read index file header on %s\n",vmsdev);
570 
571 	if ( !getlb(ifhbn+3,(char *)&mfdh) )
572 		err1("Can't read mfd header on %s",vmsdev);
573 
574 	return(1);
575 }
576 
577 
578 /*
579  *  Locate the directory whose name is pointed to by "dir"
580  */
581 
582 #define alphanum
583 
finddir(void)584 int finddir(void)
585 {
586 #define direrr(msg,dirname,ptr) { dirmsg(msg,dirname,ptr); return(0); }
587 	struct header	*hp = &mfdh;
588 	register char	*p = dir;
589 	register char	*q;
590 	char		*strchr();
591 	int		nch;
592 	struct filnam	dirfn;
593 	ushort		dirfnum;
594 	ushort		search();
595 	int		gh;
596 
597 	do {
598 		for ( q=p; alphnum(*q); ++q )
599 			;
600 		if ( ( *q && *q!='.' ) || (nch=q-p) == 0 || nch > 9 )
601 			err1("Invalid directory ([%s])",dir);
602 		strncpy(dirfn.f_nam,p,nch);
603 		dirfn.f_nam[nch] = '\0';
604 		strcat(dirfn.f_nam,".DIR");
605 		dirfn.f_ver = 1;
606 		if ( !(dirfnum=search(hp,&dirfn)) )
607 			direrr("Directory [%s] does not exist",dir,q);
608 		if ( !(gh=gethdr(dirfnum,(hp=(&dirh)))) )
609 			direrr("Can't get file header for directory [%s]",dir,q);
610 		if ( gh == -1 )
611 			direrr("No access privilege for directory [%s]",dir,q);
612 		p = q + 1;
613 	} while (*q);
614 	dirfound = 1;
615 	return(1);
616 }
617 
618 
619 /*
620  *  Error accessing a directory
621  */
622 
dirmsg(char * msg,char * dirname,char * ptr)623 void dirmsg(char *msg,char *dirname,char *ptr)
624 {
625 	char	c;
626 
627 	c = *ptr;
628 	*ptr = '\0';
629 	errmsg(msg,dirname);
630 	*ptr = c;
631 }
632 
633 
634 /*
635  *  Convert file name, type, and version number to "struct filnam" format
636  */
637 
convert(char * fl,char * tp,char * vr,struct filnam * f)638 int convert(char *fl,char *tp,char *vr,struct filnam *f)
639 {
640 	register char *p;
641 
642 	if ( strlen(fl) > 9 )
643 		err0("Filename longer than 9 characters");
644 	if ( strlen(tp) > 3 )
645 		err0("File type longer than 3 characters");
646 	strcpy(f->f_nam,fl);
647 	strcat(f->f_nam,".");
648 	strcat(f->f_nam,tp);
649 	for ( p=f->f_nam; *p; ++p )		/* This code is needed since */
650 		if ( 'a' <= *p && *p <= 'z' )	/* VMS loves to SHOUT at you */
651 			*p += 'A' - 'a';	/* in UPPER CASE all the time */
652 	for ( f->f_ver=0; *vr; ) {
653 		if ( !decimal(*vr) )
654 			err0("Non-digit in version number");
655 		f->f_ver *= 10;
656 		f->f_ver += *vr++ - '0';
657 	}
658 	return(1);
659 }
660 
661 
662 /*
663  *  Search a directory (identified by dhp) for a filename
664  */
665 
666 ushort
search(dhp,fn)667 search(dhp,fn)
668 register struct header	*dhp;
669 register struct filnam	*fn;
670 {
671 	int				len;
672 	int				bod;
673 	register struct directory	*de;
674 	struct directory		*getde();
675 	register struct dirval		*vp;
676 	register struct dirval		*vplim;
677 
678 	len = strlen(fn->f_nam);
679 	for ( bod=1; de=getde(dhp,bod); bod=0 ) {
680 		if ( de->d_nbc!=len || strncmp(de->d_fname,fn->f_nam,len)!=0 )
681 			continue;
682 		vp = (struct dirval *) ( de->d_fname + ((de->d_nbc+1)&0376) );
683 		if ( !fn->f_ver )
684 			return(vp->d_fid.f_num);
685 		for ( vplim=(struct dirval *)((char *)(&de->d_vrlm)+de->d_rbc); vp<vplim; ++vp
686  ) {
687 			if ( vp->d_ver > fn->f_ver )
688 				continue;
689 			if ( vp->d_ver == fn->f_ver )
690 				return(vp->d_fid.f_num);
691 			return(0);
692 		}
693 		return(0);
694 	}
695 	return(0);
696 }
697 
698 
699 /*
700  *  Open output file
701  */
702 
openout(void)703 int openout(void)
704 {
705 	char	outfile[256];
706 
707 	if ( outdest == STDOUT ) {
708 		of = stdout;
709 		return(1);
710 	}
711 
712 	strcpy(outfile,fil);
713 	strcat(outfile,".");
714 	strcat(outfile,typ);
715 	if (okwrite(outfile) == 0		/* ntw */
716 	||   (of=fopen(outfile,"w")) == NULL )
717 		err0("Can't open output file");
718 	return(1);
719 }
720 
721 
722 /*
723  * see if ok to write/create this file
724  * needed because we might be setuid or setgid
725  * to get at the special files for disks
726  * nb we assume the file is in the working directory
727  * always true at the moment;  might neeed more mess in future
728  */
729 
okwrite(char * file)730 int okwrite(char *file)
731 {
732 
733 	if (access(file, 02) == 0)
734 		return (1);		/* exists and is writeable */
735 	if (access(file, 0) == 0)
736 		return (0);		/* exists although not writeable */
737 	if (index(file, '/'))
738 		return (0);		/* snh */
739 #ifdef VMS
740 	return(1);
741 #endif
742 
743 	if (access(".", 02) == 0)
744 		return (1);		/* file doesn't exist and can create it */
745 	return (0);
746 }
747 
748 
749 /*
750  *  Copy input file to output destination
751  */
752 
copyfile(void)753 int copyfile(void)
754 {
755 	long		eofblk;
756 	register long	block = 0;
757 	register long	b = 0;
758 	char		buf[BUFSIZE];
759 	int		nbytes = BUFSIZE;
760 	register char	*p;
761 
762 	if ( xfermode == BINARY )
763 		err0("Binary mode not yet supported");
764 	if ( xfermode != IMGFULL )
765 		eofblk = ( (long)fileh.h_rms.f_heof[0] << 16 ) + fileh.h_rms.f_heof[1];
766 	while ( getvb(++block,buf,&fileh) ) {
767 		if ( xfermode == IMGFULL ) {
768 			if ( fwrite(buf,1,BUFSIZE,of) == BUFSIZE )
769 				continue;
770 			err0("write error");
771 		}
772 		if ( ++b > eofblk )
773 			return(1);
774 		if ( b == eofblk )
775 			nbytes = fileh.h_rms.f_ffby;
776 		if ( xfermode == IMGRMS ) {
777 			if ( fwrite(buf,1,nbytes,of) == nbytes )
778 				continue;
779 			err0("write error");
780 		}
781 		for ( p=buf; p<buf+nbytes; )
782 			putch(*p++);
783 	}
784 	return(1);
785 }
786 
787 
788 /*
789  *  Process next character from input file
790  *  for text mode
791  */
792 
793 /*
794  * possible states of the machine:
795  */
796 
797 #define INIT	0	/* waiting for the beginning of a record */
798 #define COUNT	1	/* in byte count */
799 #define LINENO	2	/* in line number */
800 #define DATA	3	/* in data */
801 #define NULLPAD	4	/* eating the padding null at the end */
802 
putch(char c)803 void putch(char c)
804 {
805 	static unsigned	count;
806 	static int	state = INIT;
807 	static int	nextstate;
808 	static int	lnbytes;
809 
810 	switch (state) {
811 	case INIT:
812 		count = (c&0377);
813 		state = COUNT;
814 		break;
815 
816 	case COUNT:
817 		if ( (count+=((c&0377)<<8)) == 0 ) {
818 			putc('\n',of);
819 			state = INIT;
820 		} else {
821 			if (rmlineno == 0)
822 				state = DATA;
823 			else {
824 				lnbytes = 0;
825 				state = LINENO;
826 			}
827 			nextstate = INIT;
828 			if ( count&1 )
829 				nextstate = NULLPAD;
830 		}
831 		break;
832 
833 	case LINENO:
834 		if (lnbytes == 0)
835 			lnbytes++;
836 		else
837 			state = DATA;
838 		if (--count == 0) {
839 			putc('\n', of);
840 			state = INIT;
841 		}
842 		break;
843 
844 	case DATA:
845 		putc(c,of);
846 		if ( --count == 0 ) {
847 			state = nextstate;
848 			putc('\n',of);
849 		}
850 		break;
851 
852 	case NULLPAD:
853 		state = INIT;
854 		break;
855 
856 	default:
857 		errmsg("internal error in putch");
858 		abort();
859 	}
860 }
861 
862 
863 /*
864  *  List contents of a UFD
865  */
866 
listdir(void)867 void listdir(void)
868 {
869 	register int			bod;
870 	register struct directory	*de;
871 	struct directory		*getde();
872 	register struct dirval		*vp;
873 	register struct dirval		*vplim;
874 
875 	for ( bod=1; de=getde(&dirh,bod); bod=0 ) {
876 		vp = (struct dirval *) ( de->d_fname + ((de->d_nbc+1)&0376) );
877 		vplim = (struct dirval *) ((char *)(&de->d_vrlm)+de->d_rbc);
878 		for ( ; vp<vplim; ++vp )
879 			prtfn(de,vp);
880 	}
881 }
882 
883 
884 /*
885  *  Write filename to standard output
886  */
887 
prtfn(struct directory * de,struct dirval * vp)888 void prtfn(struct directory *de,struct dirval *vp)
889 {
890 	register char	*p;
891 	register int	i;
892 
893 	for ( p=de->d_fname, i=de->d_nbc; i>0; --i )
894 		putc(*p++,stdout);
895 	fprintf(stdout,";%d\n",vp->d_ver);
896 }
897 
898 
899 /*
900  *  Return pointer to next directory entry
901  */
902 
903 struct directory *
getde(dhp,bod)904 getde(dhp,bod)
905 register struct header	*dhp;
906 int			bod;
907 {
908 #define recsize (*((ushort *)de))
909 #define STOP	((ushort)0177777)
910 	static long		vb;
911 	static long		eofblk;
912 	static char		*limit;
913 	static char		dirbuf[BUFSIZE];
914 	static char		*de;
915 
916 	if ( bod ) {
917 		vb = 0;
918 		eofblk = ( (long)dhp->h_rms.f_heof[0] << 16 ) + dhp->h_rms.f_heof[1];
919 		limit = &dirbuf[BUFSIZE];
920 	}
921 	if ( bod || (de+=(recsize+2))>=limit || recsize==STOP ) {
922 		if ( ++vb == eofblk )
923 			limit = &dirbuf[dhp->h_rms.f_ffby];
924 		if ( !getvb(vb,dirbuf,dhp) || (*((ushort *)dirbuf)) == STOP)
925 			return((struct directory *)0);
926 		de = dirbuf;
927 	}
928 	if ( de >= limit )
929 		return((struct directory *)0);
930 	return((struct directory *)de);
931 }
932 
933 
934 /*
935  *  Get a file header, given the file number; check access privilege
936  */
937 
gethdr(ushort fnum,struct header * hp)938 int gethdr(ushort fnum,struct header *hp)
939 {
940 #define G_DENY	bit(8)
941 #define W_DENY	bit(12)
942 	register long	bn;
943 	int		grp;
944 	int		ogrp;
945 
946 	bn = (long)fnum + hblock.H_ibvb + hblock.H_ibsz -1;
947 	if ( !getvb(bn,(char *)hp,&indexh) )
948 		return(0);
949 	if ( !(hp->h_fpro&W_DENY) || !getuid())
950 		return(1);
951 	grp = getgid();
952 	ogrp = 64*(grp/100) + 8*((grp/10)%10) + (grp%10);
953 #ifdef VMS
954 	/* the user must have LOGIO priv to run this anyway. */
955 	return(1);
956 #endif
957 	if ( ogrp != hp->h_proj || hp->h_fpro&G_DENY )
958 		return(-1);
959 	else
960 		return(1);
961 }
962 
963 
964 /*
965  *  Routine to get specified virtual block from a file.  Returns 0
966  *  on EOF, 1 otherwise.  Note that vbn is 1-based, not 0-based.
967  */
968 
getvb(long vbn,char * buf,struct header * hp)969 int getvb(long vbn,char *buf,struct header *hp)
970 {
971 #define WTPMASK 0140000
972 #define WTP00	0000000
973 #define WTP01	0040000
974 #define WTP10	0100000
975 #define WTP11	0140000
976 	register ushort 	*rp;
977 	register long		block;
978 	register ushort 	*limit;
979 	register ushort 	wtype;
980 	register long		lbn;
981 	register long		size;
982 	ushort			getsize();
983 
984 	rp = (ushort *)hp + (hp->h_mpof&0377);
985 	block = 1;
986 	limit = rp + (hp->h_use & 0377);		/* ntw */
987 	while ( rp < limit && vbn >= ( block + (size=getsize(rp)) ) ) {
988 		wtype = (*rp) & WTPMASK;
989 		switch (wtype) {
990 			case WTP00:	rp += 1; break;
991 			case WTP01:	rp += 2; break;
992 			case WTP10:	rp += 3; break;
993 			case WTP11:	rp += 4; break;
994 		}
995 		block += size;
996 	}
997 	if ( rp >= limit )
998 		return(0);
999 	lbn = lbnbase(rp) + vbn - block;
1000 	return(getlb(lbn,buf));
1001 }
1002 
1003 
1004 /*
1005  *  Return number of blocks mapped by the current window
1006  */
1007 
1008 ushort
getsize(rp)1009 getsize(rp)
1010 register ushort *rp;
1011 {
1012 	register ushort wtype;
1013 
1014 	wtype = (*rp) & WTPMASK;
1015 	switch (wtype) {
1016 		case WTP00:	return(0);
1017 		case WTP01:	return(((*((char *)rp))&0377)+1);
1018 		case WTP10:	return(((*rp)&037777)+1);
1019 		case WTP11:	return(((((long)(*rp)&037777)<<16)+rp[1])+1);
1020 	}
1021 }
1022 
1023 
1024 /*
1025  *  Return base lbn mapped by the current window
1026  */
1027 
lbnbase(register ushort * rp)1028 long lbnbase(register ushort *rp)
1029 {
1030 	register ushort wtype;
1031 
1032 	wtype = (*rp)&WTPMASK;
1033 	switch ( wtype ) {
1034 		case WTP00:	return(0L);
1035 		case WTP01:	return(((((char *)rp)[1]&077L)<<16)+rp[1]);
1036 		case WTP10:	return((((long)rp[2])<<16)+(long)rp[1]);
1037 		case WTP11:	return((((long)rp[3])<<16)+(long)rp[2]);
1038 	}
1039 }
1040 
1041 
1042 /*
1043  *  Get block from the filesystem, given the logical block number
1044  */
1045 
getlb(long lbn,char * buf)1046 int getlb(long	lbn,char *buf)
1047 {
1048 	if ( lbn == 0L )
1049 		err0("Bad block in file");
1050 #ifdef VMS
1051 	{int retcode;
1052 	 short iosb[4];
1053 	 retcode = sys$qiow(0,vmsfd,IO$_READLBLK,&iosb,0,0,
1054 			    buf,BUFSIZE,lbn,
1055 			    0,0,0);
1056 	 if (retcode != SS$_NORMAL) err1("QIO error %d",retcode);
1057 	 if (iosb[0] != SS$_NORMAL) err1("READVBLK error %d",iosb[0]);}
1058 #else
1059 	if ( lseek(vmsfd,BUFSIZE*lbn,0) == -1L ||  read(vmsfd,buf,BUFSIZE) != BUFSIZE )
1060 		err0("Read error");
1061 #endif
1062 	return(1);
1063 }
1064 
1065 
1066 /*
1067  *  Issue an error message
1068  */
1069 
errmsg(char * msg,...)1070 void errmsg(char *msg,...)
1071 {va_list arglist;
1072  fprintf(stderr,"%s -- ",*av);
1073  va_start(arglist,msg);
1074  vfprintf(stderr,msg,arglist);
1075  va_end(arglist);
1076  fprintf(stderr,"\n");}
1077