OSDN Git Service

update lgtm.yml
[jnethack/source.git] / util / dlb_main.c
1 /* NetHack 3.6  dlb_main.c      $NHDT-Date: 1570258542 2019/10/05 06:55:42 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.13 $ */
2 /* Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1993. */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /* data librarian; only useful if you are making the library version, DLBLIB
6  */
7
8 #include "config.h"
9 #include "dlb.h"
10 #if !defined(O_WRONLY) && !defined(MAC) && !defined(AZTEC_C)
11 #include <fcntl.h>
12 #endif
13 #if defined(__DJGPP__)
14 #include <string.h>
15 #endif
16
17 static void FDECL(grow_ld, (libdir **, int *, int));
18 static void FDECL(xexit, (int)) NORETURN;
19
20 #ifdef DLB
21 #ifdef DLBLIB
22
23 #define DLB_DIRECTORY "Directory" /* name of lib directory */
24 #define LIBLISTFILE "dlb.lst"     /* default list file */
25
26 /* library functions (from dlb.c) */
27 extern boolean FDECL(open_library, (const char *, library *));
28 extern void FDECL(close_library, (library *));
29
30 char *FDECL(eos, (char *)); /* also used by dlb.c */
31 FILE *FDECL(fopen_datafile, (const char *, const char *));
32
33 static void FDECL(Write, (int, char *, long));
34 static void NDECL(usage) NORETURN;
35 static void NDECL(verbose_help) NORETURN;
36 static void FDECL(write_dlb_directory, (int, int, libdir *, long, long, long));
37
38 static char default_progname[] = "dlb";
39 static char *progname = default_progname;
40
41 /* fixed library and list file names - can be overridden if necessary */
42 static const char *library_file = DLBFILE;
43 static const char *list_file = LIBLISTFILE;
44
45 #ifdef AMIGA
46 static char origdir[255] = "";
47 #endif
48
49 #ifndef O_BINARY
50 #define O_BINARY 0
51 #endif
52
53 #define DLB_FILES_ALLOC 200 /* initial # of files we'll handle; can grow */
54 #define DLB_VERS 1          /* version of dlb file we will write */
55
56 /*
57  * How the file is encoded within the library.  Don't use a space
58  * because (at least) the  SunOS 4.1.3 C library will eat the white
59  * space instead of preserving it like the man page says it should.
60  */
61 #define ENC_NORMAL 'n' /* normal: not compressed in any way */
62
63 /*
64  * If you know tar, you have a small clue how to use this (note: - does
65  * NOT mean stdin/stdout).
66  *
67  * dlb COMMANDoptions arg... files...
68  * commands:
69  *  dlb x       extract all files
70  *  dlb c       build the archive
71  *  dlb t       list the archive
72  * options:
73  *  v           verbose
74  *  f file      specify archive file (default DLBFILE)
75  *  I file      specify file for list of files (default LIBLISTFILE)
76  *  C dir       chdir to dir (used ONCE, not like tar's -C)
77  */
78
79 static void
80 usage()
81 {
82     (void) printf("Usage: %s [ctxCIfv] arguments... [files...]\n", progname);
83     (void) printf("  default library is %s\n", library_file);
84     (void) printf("  default list file is %s\n", list_file);
85     xexit(EXIT_FAILURE);
86     /*NOTREACHED*/
87 }
88
89 static void
90 verbose_help()
91 {
92     static const char *const long_help[] = {
93         "", "dlb COMMANDoptions args... files...", "  commands:",
94         "    dlb ?   print this text", "    dlb h   ditto",
95         "    dlb x   extract all files", "    dlb c   create the archive",
96         "    dlb t   list table of contents", "  options:",
97         "    v       verbose operation",
98         "    f file  specify archive file name",
99         "    I file  specify file for list of file names",
100         "    C dir   change directory before processing any files", "",
101         (char *) 0
102     };
103     const char *const *str;
104
105     for (str = long_help; *str; str++)
106         (void) printf("%s\n", *str);
107     usage();
108     /*NOTREACHED*/
109 }
110
111 static void
112 Write(out, buf, len)
113 int out;
114 char *buf;
115 long len;
116 {
117 #if defined(MSDOS) && !defined(__DJGPP__)
118     unsigned short slen;
119
120     if (len > 65534) {
121         printf("%d Length specified for write() too large for 16 bit env.",
122                len);
123         xexit(EXIT_FAILURE);
124     }
125     slen = (unsigned short) len;
126     if (write(out, buf, slen) != slen) {
127 #else
128     if (write(out, buf, len) != len) {
129 #endif
130         printf("Write Error in '%s'\n", library_file);
131         xexit(EXIT_FAILURE);
132     }
133 }
134
135 char *
136 eos(s)
137 char *s;
138 {
139     while (*s)
140         s++;
141     return s;
142 }
143
144 /* open_library(dlb.c) needs this (which normally comes from src/files.c) */
145 FILE *
146 fopen_datafile(filename, mode)
147 const char *filename, *mode;
148 {
149     return fopen(filename, mode);
150 }
151
152 #endif /* DLBLIB */
153 #endif /* DLB */
154
155 int
156 main(argc, argv)
157 int argc;
158 char **argv;
159 {
160 #ifdef DLB
161 #ifdef DLBLIB
162     int i, r;
163     int ap = 2;                            /* argument pointer */
164     int cp;                                /* command pointer */
165     int iseen = 0, fseen = 0, verbose = 0; /* flags */
166     char action = ' ';
167     library lib;
168
169     if (argc > 0 && argv[0] && *argv[0])
170         progname = argv[0];
171 #ifdef VMS
172     progname = vms_basename(progname);
173 #endif
174
175     if (argc < 2) {
176         usage();
177         /* doesn't return */
178     }
179
180     for (cp = 0; argv[1][cp]; cp++) {
181         switch (argv[1][cp]) {
182         default:
183             usage(); /* doesn't return */
184             /*NOTREACHED*/
185             break;
186         case '-':    /* silently ignore */
187             break;
188         case '?':
189         case 'h':
190             verbose_help();
191             /*NOTREACHED*/
192             break;
193         case 'I':
194             if (ap == argc)
195                 usage();
196             list_file = argv[ap++];
197             if (iseen)
198                 printf("Warning: multiple I options.  Previous ignored.\n");
199             iseen = 1;
200             break;
201         case 'f':
202             if (ap == argc)
203                 usage();
204             library_file = argv[ap++];
205 #ifdef VERSION_IN_DLB_FILENAME
206             library_file = build_dlb_filename(library_file);
207 #endif
208             if (fseen)
209                 printf("Warning: multiple f options.  Previous ignored.\n");
210             fseen = 1;
211             break;
212         case 'C':
213             if (ap == argc)
214                 usage();
215 #ifdef AMIGA
216             if (!getcwd(origdir, sizeof(origdir))) {
217                 printf("Can't get current directory.\n");
218                 xexit(EXIT_FAILURE);
219             }
220 #endif
221             if (chdir(argv[ap++])) {
222                 printf("Can't chdir to %s\n", argv[--ap]);
223                 xexit(EXIT_FAILURE);
224             }
225             break;
226         case 'v':
227             verbose = 1;
228             break;
229         case 't':
230         case 'c':
231         case 'x':
232             if (action != ' ') {
233                 printf("Only one of t,x,c may be specified.\n");
234                 usage();
235             }
236             action = argv[1][cp];
237             break;
238         }
239     }
240
241     if (argv[ap] && iseen) {
242         printf("Too many arguments.\n");
243         xexit(EXIT_FAILURE);
244     }
245
246     switch (action) {
247     default:
248         printf("Internal error - action.\n");
249         xexit(EXIT_FAILURE);
250         /*NOTREACHED*/
251         break;
252
253     case 't': /* list archive */
254         if (!open_library(library_file, &lib)) {
255             printf("Can't open dlb file\n");
256             xexit(EXIT_FAILURE);
257         }
258
259         for (i = 0; i < lib.nentries; i++) {
260             if (verbose)
261                 printf("%-14s %6ld %6ld\n", lib.dir[i].fname,
262                        lib.dir[i].foffset, lib.dir[i].fsize);
263             else
264                 printf("%s\n", lib.dir[i].fname);
265         }
266
267         if (verbose)
268             printf("Revision:%ld  File count:%ld  String size:%ld\n", lib.rev,
269                    lib.nentries, lib.strsize);
270
271         close_library(&lib);
272         /* xexit(EXIT_SUCCESS); */
273         break;
274
275     case 'x': { /* extract archive contents */
276         int f, n;
277         long remainder, total_read;
278         char buf[BUFSIZ];
279
280         if (!open_library(library_file, &lib)) {
281             printf("Can't open dlb file\n");
282             xexit(EXIT_FAILURE);
283         }
284
285         for (i = 0; i < lib.nentries; i++) {
286             if (argv[ap]) {
287                 /* if files are listed, see if current is wanted */
288                 int c;
289                 for (c = ap; c < argc; c++)
290                     if (!FILENAME_CMP(lib.dir[i].fname, argv[c]))
291                         break;
292                 if (c == argc)
293                     continue; /* skip */
294             } else if (!FILENAME_CMP(lib.dir[i].fname, DLB_DIRECTORY)) {
295                 /*
296                  * Don't extract the directory unless the user
297                  * specifically asks for it.
298                  *
299                  * Perhaps we should never extract the directory???
300                  */
301                 continue;
302             }
303             fseek(lib.fdata, lib.dir[i].foffset, SEEK_SET);
304
305             f = open(lib.dir[i].fname,
306                      O_WRONLY | O_TRUNC | O_BINARY | O_CREAT, 0640);
307             if (f < 0) {
308                 printf("Can't create '%s'\n", lib.dir[i].fname);
309                 xexit(EXIT_FAILURE);
310             }
311
312             /* read chunks from library and write them out */
313             total_read = 0;
314             do {
315                 remainder = lib.dir[i].fsize - total_read;
316                 if (remainder > (long) sizeof(buf))
317                     r = (int) sizeof(buf);
318                 else
319                     r = remainder;
320
321                 n = fread(buf, 1, r, lib.fdata);
322                 if (n != r) {
323                     printf("Read Error in '%s'\n", lib.dir[i].fname);
324                     xexit(EXIT_FAILURE);
325                 }
326                 if (write(f, buf, n) != n) {
327                     printf("Write Error in '%s'\n", lib.dir[i].fname);
328                     xexit(EXIT_FAILURE);
329                 }
330
331                 total_read += n;
332             } while (total_read != lib.dir[i].fsize);
333
334             (void) close(f);
335
336             if (verbose)
337                 printf("x %s\n", lib.dir[i].fname);
338         }
339
340         close_library(&lib);
341         /* xexit(EXIT_SUCCESS); */
342         break;
343     }
344
345     case 'c': /* create archive */
346     {
347         libdir *ld = 0;
348         int ldlimit = 0;
349         char buf[BUFSIZ];
350         int fd, out, nfiles = 0;
351         long dir_size, slen, flen, fsiz;
352         boolean rewrite_directory = FALSE;
353
354         /*
355          * Get names from either/both an argv list and a file
356          * list.  This does not do any duplicate checking
357          */
358
359         grow_ld(&ld, &ldlimit, DLB_FILES_ALLOC);
360
361         /* get file name in argv list */
362         if (argv[ap]) {
363             for (; ap < argc; ap++, nfiles++) {
364                 if (nfiles == ldlimit)
365                     grow_ld(&ld, &ldlimit, DLB_FILES_ALLOC / 5);
366                 ld[nfiles].fname = (char *) alloc(strlen(argv[ap]) + 1);
367                 Strcpy(ld[nfiles].fname, argv[ap]);
368             }
369         }
370
371         if (iseen) {
372             /* want to do a list file */
373             FILE *list = fopen(list_file, "r");
374             if (!list) {
375                 printf("Can't open %s\n", list_file);
376                 xexit(EXIT_FAILURE);
377             }
378
379             /* get file names, one per line */
380             for (; fgets(buf, sizeof(buf), list); nfiles++) {
381                 if (nfiles == ldlimit)
382                     grow_ld(&ld, &ldlimit, DLB_FILES_ALLOC / 5);
383                 *(eos(buf) - 1) = '\0'; /* strip newline */
384                 ld[nfiles].fname = (char *) alloc(strlen(buf) + 1);
385                 Strcpy(ld[nfiles].fname, buf);
386             }
387             fclose(list);
388         }
389
390         if (nfiles == 0) {
391             printf("No files to archive\n");
392             xexit(EXIT_FAILURE);
393         }
394
395         /*
396          * Get file sizes and name string length.  Don't include
397          * the directory information yet.
398          */
399         for (i = 0, slen = 0, flen = 0; i < nfiles; i++) {
400             fd = open(ld[i].fname, O_RDONLY | O_BINARY, 0);
401             if (fd < 0) {
402                 printf("Can't open %s\n", ld[i].fname);
403                 xexit(EXIT_FAILURE);
404             }
405             ld[i].fsize = lseek(fd, 0, SEEK_END);
406             ld[i].foffset = flen;
407
408             slen += strlen(ld[i].fname); /* don't add null (yet) */
409             flen += ld[i].fsize;
410             close(fd);
411         }
412
413         /* open output file */
414         out = open(library_file,
415                    O_RDWR | O_TRUNC | O_BINARY | O_CREAT, FCMASK);
416         if (out < 0) {
417             printf("Can't open %s for output\n", library_file);
418             xexit(EXIT_FAILURE);
419         }
420
421         /* caculate directory size */
422         dir_size = 40                    /* header line (see below) */
423                    + ((nfiles + 1) * 11) /* handling+file offset+SP+newline */
424                    + slen + strlen(DLB_DIRECTORY); /* file names */
425
426         /* write directory */
427         write_dlb_directory(out, nfiles, ld, slen, dir_size, flen);
428
429         flen = 0L;
430         /* write each file */
431         for (i = 0; i < nfiles; i++) {
432             fd = open(ld[i].fname, O_RDONLY | O_BINARY, 0);
433             if (fd < 0) {
434                 printf("Can't open input file '%s'\n", ld[i].fname);
435                 xexit(EXIT_FAILURE);
436             }
437             if (verbose)
438                 printf("%s\n", ld[i].fname);
439
440             fsiz = 0L;
441             while ((r = read(fd, buf, sizeof buf)) != 0) {
442                 if (r == -1) {
443                     printf("Read Error in '%s'\n", ld[i].fname);
444                     xexit(EXIT_FAILURE);
445                 }
446                 if (write(out, buf, r) != r) {
447                     printf("Write Error in '%s'\n", ld[i].fname);
448                     xexit(EXIT_FAILURE);
449                 }
450                 fsiz += r;
451             }
452             (void) close(fd);
453             if (fsiz != ld[i].fsize)
454                 rewrite_directory = TRUE;
455             /* in case directory rewrite is needed */
456             ld[i].fsize = fsiz;
457             ld[i].foffset = flen;
458             flen += fsiz;
459         }
460
461         if (rewrite_directory) {
462             if (verbose)
463                 printf("(rewriting dlb directory info)\n");
464             (void) lseek(out, 0, SEEK_SET); /* rewind */
465             write_dlb_directory(out, nfiles, ld, slen, dir_size, flen);
466         }
467
468         for (i = 0; i < nfiles; i++)
469             free((genericptr_t) ld[i].fname), ld[i].fname = 0;
470         free((genericptr_t) ld), ldlimit = 0;
471
472         (void) close(out);
473         /* xexit(EXIT_SUCCESS); */
474         break;
475     } /* case 'c' */
476     } /* switch */
477 #endif /* DLBLIB */
478 #endif /* DLB */
479
480     xexit(EXIT_SUCCESS);
481     /*NOTREACHED*/
482     return 0;
483 }
484
485 #ifdef DLB
486 #ifdef DLBLIB
487
488 static void
489 grow_ld(ld_p, ldlimit_p, alloc_incr)
490 libdir **ld_p;
491 int *ldlimit_p;
492 int alloc_incr;
493 {
494     static libdir zerolibdir;
495     int i = 0, newlimit = *ldlimit_p + alloc_incr;
496     libdir *newld = (libdir *) alloc(newlimit * sizeof *newld);
497
498     if (*ld_p) {
499         for (; i < *ldlimit_p; ++i)
500             newld[i] = (*ld_p)[i];
501         free((genericptr_t) *ld_p);
502     }
503     *ld_p = newld, *ldlimit_p = newlimit;
504     for (; i < *ldlimit_p; ++i)
505         (*ld_p)[i] = zerolibdir;
506 }
507
508 static void
509 write_dlb_directory(out, nfiles, ld, slen, dir_size, flen)
510 int out, nfiles;
511 libdir *ld;
512 long slen, dir_size, flen;
513 {
514     char buf[BUFSIZ];
515     int i;
516
517     sprintf(buf, "%3ld %8ld %8ld %8ld %8ld\n",
518             (long) DLB_VERS,   /* version of dlb file */
519             (long) nfiles + 1, /* # of entries (includes directory) */
520                                /* string length + room for nulls */
521             (long) slen + (long) strlen(DLB_DIRECTORY) + nfiles + 1,
522             (long) dir_size,         /* start of first file */
523             (long) flen + dir_size); /* total file size */
524     Write(out, buf, strlen(buf));
525
526 /* write each file entry */
527 #define ENTRY_FORMAT "%c%s %8ld\n"
528     sprintf(buf, ENTRY_FORMAT, ENC_NORMAL, DLB_DIRECTORY, (long) 0);
529     Write(out, buf, strlen(buf));
530     for (i = 0; i < nfiles; i++) {
531         sprintf(buf, ENTRY_FORMAT, ENC_NORMAL, /* encoding */
532                 ld[i].fname,                   /* name */
533                 ld[i].foffset + dir_size);     /* offset */
534         Write(out, buf, strlen(buf));
535     }
536 }
537
538 #endif /* DLBLIB */
539 #endif /* DLB */
540
541 static void
542 xexit(retcd)
543 int retcd;
544 {
545 #ifdef DLB
546 #ifdef AMIGA
547     if (origdir[0])
548         chdir(origdir);
549 #endif
550 #endif
551     exit(retcd);
552     /*NOTREACHED*/
553 }
554
555 #ifdef AMIGA
556 #include "date.h"
557 const char amiga_version_string[] = AMIGA_VERSION_STRING;
558 #endif
559
560 /*dlb_main.c*/