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