-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathvmsbackup.c
More file actions
3482 lines (3336 loc) · 96.5 KB
/
Copy pathvmsbackup.c
File metadata and controls
3482 lines (3336 loc) · 96.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* @file vmsbackup.c
*/
/**
*
* @mainpage
* @version Feb_2024
* @par Read Me
*
* Title:
* vmsbackup
*
* Decription:
* Program to read and decode VMS Backup images
*
* @author John Douglas CAREY.
* @author Sven-Ove Westberg (version 3.0 and up)
* @author Dave Shepperd, (DMS) vmsbackup@dshepperd.com
* (version Mar_2003 with snipits from Sven-Ove's
* version 4-1-1)
*
* https://github.com/DaveShepperd/vmsbackup
*
* Net-addess (as of 1986 or so; it is highly unlikely these still work):
* john%monu1.oz@seismo.ARPA
* luthcad!sow@enea.UUCP
* vmsbackup@dshepperd.com
*
* History:
* Version 1.0 - September 1984
* Can only read variable length records
* Version 1.1
* Cleaned up the program from the original hack
* Can now read stream files
* Version 1.2
* Now convert filename from VMS to UNIX
* and creates sub-directories
* Version 1.3
* Works on the Pyramid if SWAP is defined
* Version 1.4
* Reads files spanning multiple tape blocks
* Version 1.5
* Always reset reclen = 0 on file open
* Now output fixed length records
*
* Version 2.0 - July 1985
* VMS Version 4.0 causes a rethink !!
* Now use mtio operations instead of opening and closing file
* Blocksize now grabed from the label
*
* Version 2.1 - September 1985
* Handle variable length records of zero length.
*
* Version 2.2 - July 1986
* Handle FORTRAN records of zero length.
* Inserted exit(0) at end of program.
* Distributed program in aus.sources
*
* Version 2.3 - August 1986
* Handle FORTRAN records with record length fields
* at the end of a block
* Put debug output to a file.
* Distributed program in net.sources
*
* Version 3.0 - December 1986
* Handle multiple saveset
* Remote tape
* Interactive mode
* File name selection with meta-characters
* Convert ; to : in VMS filenames
* Flag for usage of VMS directory structure
* Flag for "useless" files eg. *.exe
* Flag for use VMS version in file names
* Flag for verbose mode
* Flag to list the contents of the tape
* Distributed to mod.sources
*
* Version 3.1 - March 2003 (DMS)
* ANSI'fy the source
* added option, -i, to read "tape file" from disk
* added option, -n, to read specific saveset from disk/tape
* changed option, -v, to accept a bit mask to enable various squawks
* Optimise the writing of data (avoid doing single char output where possible)
* Added handling of redundancy groups and duplicate and missing blocks.
* Fixed numerous crashes caused by various mis-decodes
* Record and decode errors are no longer fatal.
* Cut and pasted select features from the 4-1-1 release
* Added vms2unix date function and utime()'d extracted files.
* (NOTE: There may be a problem with this since I
* found several savesets made by VMS versions 3.x and 4.x
* where the date seemed to be ~4 years too young. Newer
* savesets [> VMS 5.0?] seem to have dates set correctly;
* Was there a change in the VMS epoch at some point?).
* Always walk the file data chain even when only doing a directory
* listing in order to identify those files which may present
* decode or other errors.
* Due to bizzare data in the VAR records found in .mai and .dir
* files, force it never to extract them or even walk the chain.
* Recover from BACKUP's occasional mistake of leaving off the
* record length bytes from record 0 of vfc files.
* Added some 'doxygen'ated (but untested) comments.
*
* Version 3.2 - April 2020 (DMS)
* Added -I, to read SIMH format 'tape file' from disk
*
* Verson 3.3 - Janurary 2022 (DMS)
* Fixed comments. Added a -S to skip to HDR1 record.
*
* Version 3.4 - November 2023 (DMS)
* Added -R to make it not lowercase filenames and to strip
* off the file version number completely and only output
* the latest version of any file.
*
* Version 3.5 - January 2024 (DMS)
* Added -L to provide lowercase directory and filenames.
*
* Version 3.6 - January 2024 (DMS)
* Added -E, reworked the -e option a bit.
* Fixed some comments.
*
* Version 3.7 - Feburary 2024 (DMS)
* Added support for optional VFC decode in output.
* Changed the way it handles record delimiters.
*
* Version 3.8 - Feburary 2024 (DMS)
* Added support for fixed length input records.
* Added support for getopt_long().
* Added a --record option.
* Added rfm/size/att to directory listing.
* Write binary output and filename if record errors
* detected.
* Updated help message.
* See README.md or vmsbackup.html for further details.
*
* Versin 3.9 - April 2024 (DMS)
* Added support for building under MSYS2 on Windows
*
* Versin 3.10 - April 2024 (DMS)
* Fixed MSYS2 builds and added ones for Linux, MinGW32 and
* PiOS (32 bit only).
*
* Versin 3.11 - April 2024 (DMS)
* Fixed MSYS2 and MinGW builds. Needed the O_BINARY and
* "b" flags.
*
* Versin 3.12 - May 2024 (DMS)
* Re-worked the method of exporting binary as a result of
* errors in VAR/VFC record structures.
*
* Installation:
*
* Computer Centre
* Monash University
* Wellington Road
* Clayton
* Victoria 3168
* AUSTRALIA
*
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <time.h>
#include <utime.h>
#if HAVE_STRERROR
#include <errno.h>
#endif
#include <sys/types.h>
#ifdef REMOTE
#include <local/rmt.h>
#endif
#include <sys/stat.h>
#if HAVE_MTIO
#include <sys/ioctl.h>
#include <sys/mtio.h>
#endif
#include <sys/file.h>
#if MSYS2 || MINGW
#define MKDIR(a,b) mkdir(a)
#define OPEN_FLAGS O_RDONLY|O_BINARY
#else
#define MKDIR(a,b) mkdir(a,b)
#define OPEN_FLAGS O_RDONLY
#endif
#ifndef n_elts
#define n_elts(x) (int)(sizeof(x)/sizeof((x)[0]))
#endif
#define INT_SIZEOF(x) (int)(sizeof(x))
extern int match ( const char *string, const char *pattern );
static int typecmp ( const char *str, int which );
#define MAX_FILENAME_LEN (128)
#define MAX_FORMAT_LEN (16)
struct bbh
{
short bbh_dol_w_size;
short bbh_dol_w_opsys;
short bbh_dol_w_subsys;
short bbh_dol_w_applic;
long bbh_dol_l_number;
char bbh_dol_t_spare_1[20];
short bbh_dol_w_struclev;
short bbh_dol_w_volnum;
long bbh_dol_l_crc;
long bbh_dol_l_blocksize;
long bbh_dol_l_flags;
char bbh_dol_t_ssname[32];
short bbh_dol_w_fid[3];
short bbh_dol_w_did[3];
char bbh_dol_t_filename[128];
char bbh_dol_b_rtype;
char bbh_dol_b_rattrib;
short bbh_dol_w_rsize;
char bbh_dol_b_bktsize;
char bbh_dol_b_vfcsize;
short bbh_dol_w_maxrec;
long bbh_dol_l_filesize;
char bbh_dol_t_spare_2[22];
short bbh_dol_w_checksum;
};
static unsigned long last_block_number;
struct brh
{
short brh_dol_w_rsize;
short brh_dol_w_rtype;
long brh_dol_l_flags;
long brh_dol_l_address;
long brh_dol_l_spare;
};
/* 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
{
short bsa_dol_w_size;
short bsa_dol_w_type;
char bsa_dol_t_text[1];
};
typedef enum
{
GET_IDLE,
GET_RCD_COUNT,
GET_VFC,
GET_DATA
} FileState_t;
struct file_details
{
time_t ctime;
time_t mtime;
time_t atime;
time_t btime;
FILE *extf;
FILE *altf;
int directory;
unsigned int size;
int nblk;
int lnch;
int allocation;
int usr;
int grp;
int recfmt;
int savRecFmt;
int recatt;
unsigned int inboundIndex; /* Index into specific byte in input file */
unsigned int outboundIndex; /* Index into specific byte in output file */
unsigned int altboundIndex;
int rec_count;
int rec_padding;
int vfcsize; /* number of VFC bytes */
char name[MAX_FILENAME_LEN+4]; /* Name from tape image */
char ufname[MAX_FILENAME_LEN+MAX_FORMAT_LEN+4]; /* Name converted to Unix */
char altUPfName[MAX_FILENAME_LEN+MAX_FORMAT_LEN+4]; /* Name converted to Unix */
char *altUfNameOnly; /* Place in altUPfName of '.' character at head of alternate filename */
char *versionPtr;
unsigned short reclen; /* length of most recent record read */
short fix;
unsigned short recsize; /* record length in FIXED and max record length in VAR and VFC formats */
int do_vfc;
unsigned char vfc0, vfc1;
int do_rat;
int do_binary;
int errorIndex;
int altErrorIndex;
int file_record_error;
int file_blk_error;
int file_size_error;
int file_format_error;
FileState_t file_state;
} file;
char *tapefile;
time_t secs_adj;
#define FAB_dol_C_RAW 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_FIX11 11 /* alternate fixed-length */
#define FAB_dol_C_MAXRFM 11 /* maximum rfm supported */
#define FAB_dol_M_MAIL (0x20) /* this bit is set on .mai files for some reason */
#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 record ID's */
#define FREC_END (0x00) /* End of list */
#define FREC_FNAME (0x2a) /* Filename */
#define FREC_UNK2b (0x2b) /* ? */
#define FREC_UNK2c (0x2c) /* ? */
#define FREC_UNK2d (0x2d) /* ? */
#define FREC_UNK2e (0x2e) /* ? */
#define FREC_UID (0x2f) /* UID */
#define FREC_UNK30 (0x30) /* ? */
#define FREC_UNK31 (0x31) /* ? */
#define FREC_UNK32 (0x32) /* ? */
#define FREC_UNK33 (0x33) /* ? */
#define FREC_FORMAT (0x34) /* Record format and stuff */
#define FREC_UNK35 (0x35) /* ? */
#define FREC_CTIME (0x36) /* Creation time */
#define FREC_MTIME (0x37) /* Modification time */
#define FREC_ATIME (0x38) /* Access time */
#define FREC_BTIME (0x39) /* Backup time */
#define FREC_UNK47 (0x47) /* ? */
#define FREC_UNK48 (0x48) /* ? */
#define FREC_DIRECTORY (0x49) /* Directory file */
#define FREC_UNK4a (0x4A) /* ? */
#define FREC_UNK4b (0x4B) /* ? */
#define FREC_UNK4e (0x4e) /* ? */
#define FREC_UNK4f (0x4F) /* ? */
#define FREC_UNK50 (0x50) /* ? */
#define FREC_UNK57 (0x57) /* ? */
/* Summary record ID's */
#define SUMM_END (0) /* end of summary list */
#define SUMM_SSNAME (1) /* saveset name */
#define SUMM_CMDLINE (2) /* command line */
#define SUMM_COMMENT (3) /* comment */
#define SUMM_USER (4) /* username of creator of ss */
#define SUMM_UID (5) /* PID/GID of creator */
#define SUMM_CTIME (6) /* creation time */
#define SUMM_OSCODE (7) /* OS code */
#define SUMM_OSCODE_VAX (0x400) /* VAX */
#define SUMM_OSCODE_AXP (0x800) /* Alpha */
#define SUMM_OSVERSION (8) /* OS version */
#define SUMM_NODENAME (9) /* nodename */
#define SUMM_PID (10) /* Processor ID */
#define SUMM_DEVICE (11) /* device used to write ss */
#define SUMM_BCKVERSION (12) /* BACKUP version */
#define SUMM_BLOCKSIZE (13) /* /BLOCKSIZE */
#define SUMM_GROUPSIZE (14) /* /GROUP */
#define SUMM_BUFFCOUNT (15) /* /BUFFER */
int fd; /* tape file descriptor */
int cDelim, dflag, eflag, iflag, Iflag, lcflag, nflag, binaryFlag, tflag, vflag, wflag, xflag, Rflag, vfcflag;
int setnr, selset, skipSet, numHdrs, saveSet_errors, total_errors;
char selsetname[14];
int skipping; /*!< Bit mask of errors as described below */
#define SKIP_TO_FILE (1) /*!< Skip to next file */
#define SKIP_TO_BLOCK (2) /*!< Skip to next block */
#define SKIP_TO_SAVESET (4) /*!< Skip to next saveset */
/* Bit mask of verbosity levels set in vflag */
#define VERB_LVL (1) /* verbose */
#define VERB_FILE_RDLVL (2) /* verbose on record read processing */
#define VERB_FILE_WRLVL (4) /* verbose on record write processing */
#define VERB_QUEUE_LVL (8) /* squawk about queue handling */
#define VERB_DEBUG_LVL (16) /* generic debug statements */
#define VERB_BLOCK_LVL (32) /* squawk during block processing */
#define VERB_DEBUG_U32 (64) /* squawk about what getu32() does */
char **gargv;
int goptind, gargc;
#define LABEL_SIZE 80
char label[32768 + LABEL_SIZE];
static int blocksize;
/*
* Someday, one might want to make MAX_BUFFCOUNT dynamic and get the actual
* value from the /BUFF field of the backup command (as reported in the
* summary record). For now, I just picked something that seemed reasonable.
*/
#ifndef MAX_BUFFCOUNT
#define MAX_BUFFCOUNT (10) /*!< Number of look ahead buffers */
#endif
/* A 'buffer' is actually a struct buff_ctl */
struct buff_ctl
{
unsigned char *buffer; /*!< pointer to buffer */
int next; /*!< index to next buffer (kept as index so we can realloc if necessary) */
int amt; /*!< amount of data in this buffer (0=tape mark) */
unsigned long blknum; /*!< block number (stored here for ease of use) */
};
static int buffalloc; /*!< size of each buffer within buff_ctl */
static int num_buffers; /*!< number of buffers currently allocated */
static int buff_cnt; /*!< buffer count spec'd with /BUFF to VMS BACKUP (from saveset) */
static struct buff_ctl *buffers; /*!< pointer to array of buffers (0th entry is unused) */
static int freebuffs; /*!< index to first item in freelist */
static int busybuffs; /*!< index to first item in busy list */
static int num_busys; /*!< number of items currently on busy queue */
/* 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. */
static unsigned long getu32 ( unsigned char *addr )
{
unsigned long ans;
ans = addr[3];
ans = (ans<<8) | addr[2];
ans = (ans<<8) | addr[1];
ans = (ans<<8) | addr[0];
if ( (vflag&VERB_DEBUG_U32) )
printf("getu32(): %p=%02X %02X %02X %02x = 0x%lX (%ld)\n", (void *)addr, addr[0], addr[1], addr[2], addr[3], ans, ans);
return ans;
}
static unsigned int getu16 ( unsigned char *addr )
{
unsigned int ans;
ans = (addr[1] << 8) | addr[0];
if ( (vflag&VERB_DEBUG_U32) )
printf("getu16(): %p=%02X %02X = 0x%X (%d)\n", (void *)addr, addr[0], addr[1], ans, ans);
return ans;
}
#define GETU16(x) getu16( (unsigned char *)&(x) )
#define GETU32(x) getu32( (unsigned char *)&(x) )
/**
* Dump the contents (indicies only) of the busy and free queues.
*
* @param which Bitmask of which queue to dump.
* @arg 1 Dump the busy queue
* @arg 2 Dump the free queue
*
* @return nothing
*/
static void dump_queues( int which )
{
if ( (vflag&VERB_QUEUE_LVL) )
{
int idx;
struct buff_ctl *bptr;
if ( (which&1) )
{
printf( "\tBusy queue (%d): ", busybuffs );
idx = busybuffs;
while ( idx )
{
printf( "%d ", idx );
bptr = buffers + idx;
idx = bptr->next;
}
printf( "\n" );
}
if ( (which&2) )
{
printf( "\tFree queue (%d): ", freebuffs );
idx = freebuffs;
while ( idx )
{
printf( "%d ", idx );
bptr = buffers + idx;
idx = bptr->next;
}
printf( "\n" );
}
}
}
/**
* Pop top item off busy queue
*
* @return Pointer to item or 0 if nothing available.
*/
static struct buff_ctl *popbusy_buff(void)
{
struct buff_ctl *bptr;
if ( !busybuffs )
{
if ( (vflag&VERB_QUEUE_LVL) )
{
printf( "popbusy_buff(): No items on queue.\n" );
dump_queues( 3 );
}
return NULL;
}
bptr = buffers + busybuffs;
busybuffs = bptr->next;
bptr->next = 0;
--num_busys;
if ( (vflag&VERB_QUEUE_LVL) )
{
printf( "popbusy_buff(): popped %d off busy queue. num_busys now %d\n",
bptr-buffers, num_busys );
dump_queues( 3 );
}
return bptr;
}
/**
* Add item to busy queue.
*
* @param bptr Pointer to item.
* @param front Flag indicating where to add.
* @arg 0 Append to end of list.
* @arg 1 Prepend to front of list.
*
* @return nothing
*/
static void add_busybuff( struct buff_ctl *bptr, int front )
{
int prev, ii;
++num_busys;
if ( (vflag&VERB_QUEUE_LVL) )
{
printf( "add_busybuff(): Added item %d to %s of busy queue. num_busys now %d\n",
bptr - buffers, front ? "head" : "tail", num_busys );
}
if ( front )
{
bptr->next = busybuffs;
busybuffs = bptr-buffers;
}
else
{
prev = 0;
ii = busybuffs;
bptr->next = 0; /* make sure this is off */
while ( ii ) /* walk the chain */
{
prev = ii; /* remember this */
ii = buffers[ii].next; /* advance to next */
}
if ( prev ) /* if there was a chain */
buffers[prev].next = bptr-buffers; /* link it */
else
busybuffs = bptr-buffers; /* else we are on top */
}
dump_queues( 3 );
}
/**
* Get an item from free queue.
*
* @return Pointer to item or NULL if free queue empty.
*/
static struct buff_ctl *getfree_buff(void)
{
struct buff_ctl *bptr;
if ( !freebuffs )
{
if ( (vflag&VERB_QUEUE_LVL) )
{
printf( "getfree_buff(): Nothing on free list!!! num_busys now %d\n", num_busys );
dump_queues( 3 );
}
return NULL;
}
bptr = buffers + freebuffs;
freebuffs = bptr->next;
bptr->next = 0;
bptr->amt = 0;
bptr->blknum = 0;
if ( (vflag&VERB_QUEUE_LVL) )
{
printf( "getfree_buff(): Extracted %d from freelist. num_busys now %d\n", bptr-buffers, num_busys );
dump_queues( 3 );
}
return bptr;
}
/**
* Put item back on free queue.
*
* @param bptr Pointer to item.
*
* @return nothing.
*/
static void free_buff( struct buff_ctl *bptr )
{
if ( bptr )
{
bptr->next = freebuffs;
freebuffs = bptr - buffers;
if ( (vflag&VERB_QUEUE_LVL) )
{
printf( "free_buff(): Put %d on freelist. num_busys now %d\n", bptr - buffers, num_busys );
dump_queues( 3 );
}
}
}
/**
* Put all buffers back on free queue.
*
* @return nothing.
*/
static void freeall( void )
{
if ( buffers )
{
struct buff_ctl *bp;
int ii;
bp = buffers+1;
for ( ii=1; ii < num_buffers-1; ++ii, ++bp )
bp->next = ii+1;
bp->next = 0;
freebuffs = 1;
busybuffs = 0;
if ( (vflag&VERB_QUEUE_LVL) )
{
printf( "freeall(): Free'd all buffers.\n" );
dump_queues( 3 );
}
}
}
/**
* Remove duplicate blocks from busy list.
*
* @return nothing.
*/
static void remove_dups( void )
{
int ii, lim, jj;
struct buff_ctl *bptr, *la;
int buffs[MAX_BUFFCOUNT]; /* place to hold clone of buffer layout */
if ( num_busys <= 1 )
return; /* nothing to do if there's only one item */
memset( buffs, 0, sizeof(buffs) ); /* preclear local array */
buffs[0] = busybuffs;
bptr = buffers + busybuffs;
lim = 1;
while ( lim < MAX_BUFFCOUNT && bptr->next ) /* clone the busy list to our local array */
{
buffs[lim++] = bptr->next;
la = buffers + bptr->next;
bptr = la;
}
if ( lim >= MAX_BUFFCOUNT && bptr->next )
{
printf( "Snark: fatal internal error. Too many items on buffer list.\n" );
skipping |= SKIP_TO_SAVESET; /* toss the remainder of this saveset */
return;
}
if ( lim != num_busys )
{
printf( "Snark: fatal internal error. busy list count (%d) != num_busys (%d).\n",
lim, num_busys );
skipping |= SKIP_TO_SAVESET; /* toss the remainder of this saveset */
return;
}
if ( lim == 2 && !bptr->amt ) /* only two items, but second is a tm */
return; /* so, nothing to do */
/* First, check each entry for a duplicate entry */
if ( (vflag&VERB_QUEUE_LVL) )
{
printf( "Before checking for duplicates:\n\tBusy queue (?): " );
for ( ii=0; ii < lim; ++ii )
printf( "%d ", buffs[ii] );
printf( "\n\tblknums: " );
for ( ii=0; ii < lim; ++ii )
{
bptr = buffers + buffs[ii];
printf( "%7ld ", bptr->blknum );
}
printf( "\n" );
dump_queues( 2 );
}
/*
* If there are duplicate blocks, the later one must win.
*/
for ( ii=0; ii < lim-1; ++ii ) /* for each item in busy list */
{
bptr = buffers + buffs[ii];
for ( jj=ii+1; jj < lim; ++jj ) /* check to see if there's a like numbered one that came later */
{
la = buffers + buffs[jj];
if ( la->amt && la->blknum == bptr->blknum )
{
if ( (vflag&VERB_FILE_RDLVL) )
printf( "Found duplicate block numbered %ld. Discarded original.\n", bptr->blknum );
buffs[ii] = buffs[jj]; /* Just place the duplicate in the original's location */
if ( lim-jj > 1 ) /* and shift what's left (if any) up one spot */
memmove( buffs+jj, buffs+jj+1, (lim-jj-1) * sizeof(int) );
--ii; /* conteract the outer for loop's ++ */
--lim; /* shrink the total by 1 */
--num_busys; /* reduce busy count too */
if ( (vflag&VERB_QUEUE_LVL) )
{
printf( "Found duplicate block numbered %ld. Discarding buffer %d\n",
bptr->blknum, bptr-buffers );
busybuffs = buffs[0]; /* fixup the busy que so it'll display correctly */
for ( jj=0; jj < lim-1; ++jj )
{
la = buffers + buffs[jj];
la->next = buffs[jj+1];
}
la = buffers + buffs[jj];
la->next = 0;
}
free_buff( bptr ); /* toss the original buffer */
break; /* look again from the beginning */
}
}
}
if ( (vflag&VERB_QUEUE_LVL) )
{
printf( "After removing duplicates:\n\tBusy queue (?): " );
for ( ii=0; ii < lim; ++ii )
printf( "%d ", buffs[ii] );
printf( "\n" );
dump_queues( 2 );
}
/*
* There may be missing blocks, so we bubble sort what's left
* in case that happens. The decoder will check for and decide
* what to do about missing blocks.
*/
for ( ii=0; ii < num_busys-1; ++ii )
{
bptr = buffers + buffs[ii];
for ( jj=ii+1; jj < num_busys; ++jj )
{
la = buffers + buffs[jj];
if ( bptr->amt && bptr->blknum > la->blknum )
{
int sav = buffs[ii];
buffs[ii] = buffs[jj];
buffs[jj] = sav;
bptr = la;
}
}
}
/* Now rebuild the linked busy list */
for ( ii=0; ii < num_busys-1; ++ii )
{
bptr = buffers + buffs[ii];
bptr->next = buffs[ii+1];
}
bptr = buffers + buffs[ii];
bptr->next = 0;
busybuffs = buffs[0];
if ( (vflag&VERB_QUEUE_LVL) )
{
printf( "After sorting:\n" );
dump_queues( 3 );
}
}
static int getRfmRatt(struct file_details *file, char *rcdFormat, int dstLen, char delim)
{
int ii,rLen;
static struct
{
int type;
const char *name;
} RcdFmts[] = {
#if 0
"UNDEF" // 0
,"FIXED" // 1
,"VAR" // 2
,"VFC" // 3
,"STMCRLF" // 4
,"STMLF" // 5
,"STMCR" // 6
#endif
{ FAB_dol_C_RAW, "UNDEF" },
{ FAB_dol_C_FIX, "FIXED" },
{ FAB_dol_C_VAR, "VAR" },
{ FAB_dol_C_VFC, "VFC" },
{ FAB_dol_C_STM, "STMCRLF" },
{ FAB_dol_C_STMLF, "STMLF" },
{ FAB_dol_C_STMCR, "STMCR" },
{ FAB_dol_C_FIX11, "FIXED" }
};
static struct
{
int mask;
const char *name;
} RcdAtts[] = {
{ 1<<FAB_dol_V_FTN, "FTN" },
{ 1<<FAB_dol_V_CR, "CR" },
{ 1<<FAB_dol_V_PRN, "PRN" },
{ 1<<FAB_dol_V_BLK, "BLK" }
};
rLen = 0;
for (ii=0; ii < n_elts(RcdFmts);++ii)
{
if ( file->savRecFmt == RcdFmts[ii].type )
{
if ( file->savRecFmt == FAB_dol_C_VFC )
rLen += snprintf(rcdFormat + rLen, dstLen - rLen, "%c%s%d%c%d", delim, RcdFmts[ii].name, file->vfcsize, delim, file->recsize);
else
rLen += snprintf(rcdFormat + rLen, dstLen - rLen, "%c%s%c%d", delim, RcdFmts[ii].name, delim, file->recsize);
break;
}
}
if ( ii >= n_elts(RcdFmts) )
rLen += snprintf(rcdFormat + rLen, dstLen - rLen, "%cUNDEF%c%d", delim, delim, file->recsize);
if ( (file->recatt&((1<<n_elts(RcdAtts))-1)) )
{
for ( ii = 0; ii < n_elts(RcdAtts); ++ii )
{
if ( (file->recatt&RcdAtts[ii].mask) )
rLen += snprintf(rcdFormat + rLen, dstLen - rLen, "%c%s", delim, RcdAtts[ii].name);
}
}
else
rLen += snprintf(rcdFormat + rLen, dstLen - rLen, "%cNONE", delim);
rcdFormat[rLen] = 0;
return rLen;
}
/**
* Open a unix file.
*
* @param ufn Pointer to place to deposit unix filename.
* @param fn Pointer to VMS filename.
* @param dirfile Flag indicating file is a directory.
* @arg 0 File is not a directory.
* @arg non-zero File is a directory.
*
* @return FILE * of open file or NULL if file not opened.
*
* @note
* Convert VMS filename, 'fn', to unix filename, 'ufn'.
* Check filename against the 'don't extract' list.
* Opens an output file only if in extract mode.
*/
static char lastFileName[256];
static int lastVersionNumber;
static FILE *openfile ( struct file_details *file )
{
char ans[80];
char *p, *q, s, *ext = NULL; /*, *justFileName; */
int procf;
char *ufn = file->ufname;
char *fn = file->name;
int dirfile = file->directory;
char rfm[MAX_FORMAT_LEN+4];
procf = 1;
/* copy whole fn to ufn and convert to lower case */
p = fn;
q = ufn;
if ( *p == '[' ) /* strip off leading '[' */
++p;
while ( *p )
{
if ( lcflag && isupper ( *p ) )
*q = tolower( *p );
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; /* both ufn and p point to path and q points to start of filename in the ufn string. */
/* Make a copy of the directory */
/* justFileName = q; */
if ( !dflag )
{
strcpy( ufn, q ); /* not keeping the directory structure so toss the path */
file->altUPfName[0] = '.'; /* alternate file starts with a '.' */
strncpy(file->altUPfName+1, q, sizeof(file->altUPfName)-2);
}
else
{
int sLen = q-ufn; /* Get length of path */
memcpy(file->altUPfName, ufn, sLen); /* duplicate the path */
file->altUPfName[sLen] = '.'; /* start alternate filename with a '.' */
file->altUfNameOnly = file->altUPfName+sLen;
strncpy(file->altUPfName + sLen + 1, q, sizeof(file->altUPfName)-sLen-2); /* copy rest of filename */
}
/* strip off the version number and possibly fix the filename's case */
while ( *q && *q != ';' )
{
if ( *q == '.' )
ext = q;
q++;
}
file->do_binary = 0;
file->do_rat = 0;
if ( !binaryFlag )
{
const char *snarkMsg=NULL;
file->do_rat = (file->recatt & ((1 << FAB_dol_V_FTN) | (1 << FAB_dol_V_CR) | (1 << FAB_dol_V_PRN)));
if ( ((file->recfmt & 0x1F) == FAB_dol_C_FIX) || ((file->recfmt & 0x1F) == FAB_dol_C_FIX11) )
snarkMsg = "Snark: process_file(): File %s is FIXED. Setting it to binary\n";
if ( !snarkMsg && /* ((file->recfmt & 0x1F) == FAB_dol_C_VAR) && */ !file->do_rat )
snarkMsg = "Snark: process_file(): File %s has no record attibutes. Setting it to binary\n";
if ( snarkMsg )
{
printf(snarkMsg, file->ufname);
file->savRecFmt = file->recfmt;
file->recfmt = FAB_dol_C_RAW;
file->do_binary = 1;
}
}
else
{
file->recfmt = FAB_dol_C_RAW;
file->do_binary = 1;
}
if ( *q == ';' )
{
file->versionPtr = q;
if ( Rflag )
{
char *endp = NULL;
int curVersion;
curVersion = strtol(q+1,&endp,10);
if ( !endp || *endp )
curVersion = 0;
*q = 0;
if ( curVersion && !strcmp(lastFileName,ufn) )
{
if ( curVersion < lastVersionNumber )
{
printf( "Skipping extraction of \"%s;%d\" because it's an older version of %s;%d.\n", p, curVersion, lastFileName, lastVersionNumber );
procf = 0;
}
else
lastVersionNumber = curVersion;
}
else
{