Subversion Repositories tendra.SVN

Rev

Rev 5 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 7u83 1
/*
6 7u83 2
 * Copyright (c) 2002-2005 The TenDRA Project <http://www.tendra.org/>.
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are met:
7
 *
8
 * 1. Redistributions of source code must retain the above copyright notice,
9
 *    this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright notice,
11
 *    this list of conditions and the following disclaimer in the documentation
12
 *    and/or other materials provided with the distribution.
13
 * 3. Neither the name of The TenDRA Project nor the names of its contributors
14
 *    may be used to endorse or promote products derived from this software
15
 *    without specific, prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
18
 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
21
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
 * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 *
29
 * $Id$
30
 */
31
/*
2 7u83 32
    		 Crown Copyright (c) 1997
6 7u83 33
 
2 7u83 34
    This TenDRA(r) Computer Program is subject to Copyright
35
    owned by the United Kingdom Secretary of State for Defence
36
    acting through the Defence Evaluation and Research Agency
37
    (DERA).  It is made available to Recipients with a
38
    royalty-free licence for its use, reproduction, transfer
39
    to other parties and amendment for any purpose not excluding
40
    product development provided that any such use et cetera
41
    shall be deemed to be acceptance of the following conditions:-
6 7u83 42
 
2 7u83 43
        (1) Its Recipients shall ensure that this Notice is
44
        reproduced upon any copies or amended versions of it;
6 7u83 45
 
2 7u83 46
        (2) Any amended version of it shall be clearly marked to
47
        show both the nature of and the organisation responsible
48
        for the relevant amendment or amendments;
6 7u83 49
 
2 7u83 50
        (3) Its onward transfer from a recipient to another
51
        party shall be deemed to be that party's acceptance of
52
        these conditions;
6 7u83 53
 
2 7u83 54
        (4) DERA gives no warranty or assurance as to its
55
        quality or suitability for any purpose and DERA accepts
56
        no liability whatsoever in relation to any use to which
57
        it may be put.
58
*/
59
 
60
 
61
#include "config.h"
62
#include "external.h"
63
#include "filename.h"
64
#include "list.h"
65
#include "archive.h"
66
#include "flags.h"
67
#include "options.h"
68
#include "utility.h"
69
 
70
 
71
/*
6 7u83 72
 * ARCHIVE BLOCK SIZE
73
 *
74
 * This defines the size of the chunks read and written by the archiving
75
 * routines. It should not exceed buffer_size.
76
 */
2 7u83 77
 
78
#define block_size		buffer_size
79
 
80
 
81
/*
6 7u83 82
 * READ A GIVEN FILE FROM A STREAM
83
 *
84
 * This routine reads n characters from the file f into a new file named nm. It
85
 * returns a nonzero value if an error occurs.
86
 */
2 7u83 87
 
6 7u83 88
static int
89
read_file(char *nm, char *w, long n, FILE *f)
2 7u83 90
{
6 7u83 91
	if (dry_run) {
92
		if (fseek(f, n, SEEK_CUR)) {
93
			error(SERIOUS, "Error when stepping over '%s'", nm);
94
			return (1);
95
		}
96
	} else {
97
		size_t m = (size_t)n;
98
		FILE *g = fopen(nm, w);
99
		if (g == null) {
100
			error(SERIOUS,
101
			      "Can't open copy destination file, '%s'", nm);
102
			return (1);
103
		}
104
		while (m) {
105
			size_t r = m, s;
106
			pointer p = (pointer)buffer;
107
			if (r > (size_t)block_size) {
108
				r = (size_t)block_size;
109
			}
110
			s = fread(p, sizeof(char), r, f);
111
			if (s != r) {
112
				error(SERIOUS,
113
				      "Reading error when creating '%s'", nm);
114
				IGNORE fclose(g);
115
				return (1);
116
			}
117
			s = fwrite(p, sizeof(char), r, g);
118
			if (s != r) {
119
				error(SERIOUS,
120
				      "Writing error when creating '%s'", nm);
121
				IGNORE fclose(g);
122
				return (1);
123
			}
124
			m = (size_t)(m - r);
125
		}
126
		IGNORE fclose(g);
2 7u83 127
	}
6 7u83 128
	return (0);
2 7u83 129
}
130
 
131
 
132
/*
6 7u83 133
 * WRITE A GIVEN FILE TO A STREAM
134
 *
135
 * This routine copies the file named nm into the file f. It returns a nonzero
136
 * value if an error occurs.
137
 */
2 7u83 138
 
6 7u83 139
static int
140
write_file(char *nm, char *rd, FILE *f)
2 7u83 141
{
6 7u83 142
	FILE *g;
143
	size_t n, m;
144
	pointer p = (pointer)buffer;
145
	if (dry_run) {
146
		return (0);
2 7u83 147
	}
6 7u83 148
	g = fopen(nm, rd);
149
	if (g == null) {
150
		error(SERIOUS, "Can't open copy source file, '%s'", nm);
151
		return (1);
152
	}
153
	while (n = fread(p, sizeof(char), (size_t)block_size, g), n) {
154
		m = fwrite(p, sizeof(char), n, f);
155
		if (m != n) {
156
			error(SERIOUS, "Writing error when copying '%s'", nm);
157
			IGNORE fclose(g);
158
			return (0);
159
		}
160
	}
161
	IGNORE fclose(g);
162
	return (0);
2 7u83 163
}
164
 
165
 
166
/*
6 7u83 167
 * CAT A FILE
168
 *
169
 * This routine copies the file named nm to the standard output. It returns a
170
 * nonzero value if an error occurs.
171
 */
2 7u83 172
 
6 7u83 173
int
174
cat_file(char *nm)
2 7u83 175
{
6 7u83 176
	return (write_file(nm, "r", stdout));
2 7u83 177
}
178
 
179
 
180
/*
6 7u83 181
 * CREATE A DIRECTORY
182
 *
183
 * This routine creates a directory called nm, returning zero if it is
184
 * successful. Two alternative versions of the routine are provided. The first
185
 * is POSIX compliant and uses mkdir and various mode constants from
186
 * sys/stat.h. The second raises an error - in this case the mkdir function
187
 * should be implemented by an external call.
188
 */
2 7u83 189
 
6 7u83 190
int
191
make_dir(char *nm)
2 7u83 192
{
6 7u83 193
	if (dry_run) {
194
		return (0);
195
	}
2 7u83 196
#if FS_STAT
6 7u83 197
	{
198
		mode_t m = (mode_t)(S_IRWXU | S_IRWXG | S_IRWXO);
199
		int e = mkdir(nm, m);
200
		return (e);
201
	}
2 7u83 202
#else
6 7u83 203
	{
204
		error(INTERNAL, "Built-in mkdir function not implemented");
205
		return (1);
206
	}
2 7u83 207
#endif
208
}
209
 
210
 
211
/*
6 7u83 212
 * MOVE A FILE
213
 *
214
 * This routine moves the file named from to the file named to, returning zero
215
 * if it is successful. Normally the files will be on different filesystems, so
216
 * we can't always use rename.
217
 */
2 7u83 218
 
6 7u83 219
int
220
move_file(char *from, char *to)
2 7u83 221
{
6 7u83 222
	int e;
223
	FILE *f;
224
	if (dry_run) {
225
		return (0);
226
	}
227
	if (streq(from, to)) {
228
		return (0);
229
	}
2 7u83 230
#if FS_STAT
6 7u83 231
	if (rename(from, to) == 0) {
232
		return (0);
233
	}
234
	if (errno != EXDEV) {
235
		error(SERIOUS, "Can't rename '%s' to '%s'", from, to);
236
		return (1);
237
	}
2 7u83 238
#endif
6 7u83 239
	f = fopen(to, "wb");
240
	if (f == null) {
241
		error(SERIOUS, "Can't open copy destination file, '%s'", to);
242
		return (1);
243
	}
244
	e = write_file(from, "rb", f);
245
	IGNORE fclose(f);
246
	if (e) {
247
		return (e);
248
	}
249
	if (remove(from)) {
250
		error(SERIOUS, "Can't remove source file, '%s'", from);
251
		return (1);
252
	}
253
	return (0);
2 7u83 254
}
255
 
256
 
257
/*
6 7u83 258
 * REMOVE A FILE
259
 *
260
 * This routine removes the file or directory named nm, returning zero if it is
261
 * successful. Two alternative versions of the routine are provided. The first
262
 * is POSIX compliant and uses stuff from sys/stat.h and dirent.h. The second
263
 * raises an error - in this case the remove function should be implemented by
264
 * an external call.
265
 */
2 7u83 266
 
6 7u83 267
int
268
remove_file(char *nm)
2 7u83 269
{
6 7u83 270
	if (dry_run) {
271
		return (0);
272
	}
2 7u83 273
#if FS_DIRENT
6 7u83 274
	{
275
		struct stat st;
276
		int e = stat(nm, &st);
277
		if (e != -1) {
278
			mode_t m = (mode_t)st.st_mode;
279
			if (S_ISDIR(m)) {
280
				DIR *d = opendir(nm);
281
				if (d == null) {
282
					e = 1;
283
				} else {
284
					char *p;
285
					struct dirent *t;
286
					char buff [1000];
287
					IGNORE sprintf(buff, "%s/", nm);
288
					p = buff + strlen(buff);
289
					while (t = readdir(d), t != null) {
290
						char *dnm = t->d_name;
291
						if (!streq(dnm, ".") &&
292
						    !streq(dnm, "..")) {
293
							IGNORE strcpy(p, dnm);
294
							if (remove_file(buff)) {
295
								e = 1;
296
							}
297
						}
298
					}
299
					IGNORE closedir(d);
300
					if (rmdir(nm)) {
301
						e = 1;
302
					}
303
				}
304
			} else {
305
				e = remove(nm);
306
			}
2 7u83 307
		} else {
6 7u83 308
			/* If the file didn't exist, don't worry */
309
			if (errno == ENOENT) {
310
				return (0);
2 7u83 311
			}
312
		}
6 7u83 313
		if (e) {
314
			error(SERIOUS, "Can't remove '%s'", nm);
315
			return (1);
316
		}
317
		return (0);
2 7u83 318
	}
6 7u83 319
#else
320
	{
321
		error(INTERNAL, "Built-in remove function not implemented");
322
		return (1);
2 7u83 323
	}
324
#endif
325
}
326
 
327
 
328
/*
6 7u83 329
 * TOUCH A FILE
330
 *
331
 * This routine touches the file called nm. It returns 0 if it is successful.
332
 */
2 7u83 333
 
6 7u83 334
int
335
touch_file(char *nm, char *opt)
2 7u83 336
{
6 7u83 337
	if (!dry_run) {
338
		FILE *f = fopen(nm, "w");
339
		if (f == null) {
340
			error(SERIOUS, "Can't touch file, '%s'", nm);
341
		}
342
		if (opt && streq(opt, "-k")) {
343
			/* This is an empty C spec file */
344
			static unsigned char cs [] = {
345
				0x80
346
			};
347
			size_t s1 = sizeof(cs [0]);
348
			size_t sn = (size_t)(sizeof(cs) / s1);
349
			IGNORE fwrite((pointer)cs, s1, sn, f);
350
		} else {
351
			IGNORE fputs("EMPTY\n", f);
352
		}
353
		IGNORE fclose(f);
2 7u83 354
	}
6 7u83 355
	return (0);
2 7u83 356
}
357
 
358
 
359
/*
6 7u83 360
 * POOR MAN'S TEMPNAM FUNCTION
361
 *
362
 * The token temporary_name can be defined to be either tempnam (which is in
363
 * XPG3 but not POSIX) or this routine, which is designed to serve a similar
364
 * purpose. The routine uses tmpnam (which is in ANSI) to create a temporary
365
 * file name, and appends the suffix pfx to this name. The dir argument is not
366
 * used.
367
 */
2 7u83 368
 
369
#if !FS_TEMPNAM
370
 
6 7u83 371
char *
372
like_tempnam(char *dir, char *pfx) /* ARGSUSED */
2 7u83 373
{
6 7u83 374
	static char letter = 'a';
375
	char *p = buffer;
376
	UNUSED(dir);
377
	IGNORE tmpnam(p);
378
	p += strlen(p);
379
	p [0] = letter;
380
	p [1] = '.';
381
	IGNORE strcpy(p + 2, pfx);
382
	letter = (char)(letter + 1);
383
	return (string_copy(buffer));
2 7u83 384
}
385
 
386
#endif
387
 
388
 
389
 
390
/*
6 7u83 391
 * FIND THE SIZE OF A FILE
392
 *
393
 * This routine calculates the length of a file, returning zero for
394
 * non-existant files. Two versions of the routine are provided. The first is
395
 * POSIX compliant and uses stat from sys/stat.h to access the length directly.
396
 * The second just reads the file and counts the number of characters.
397
 */
2 7u83 398
 
6 7u83 399
long
400
file_size(char *nm)
2 7u83 401
{
402
#if FS_STAT
6 7u83 403
	{
404
		struct stat st;
405
		int e = stat(nm, &st);
406
		if (e == -1) {
407
			return (0);
408
		}
409
		return ((long)st.st_size);
410
	}
2 7u83 411
#else
6 7u83 412
	{
413
		size_t n = 0, m;
414
		pointer p = (pointer)buffer;
415
		FILE *f = fopen(nm, "rb");
416
		if (f == null) {
417
			return (0);
418
		}
419
		while (m = fread(p, sizeof(char), block_size, f), m != 0) {
420
			n = (size_t)(n + m);
421
		}
422
		IGNORE fclose(f);
423
		return ((long)n);
2 7u83 424
	}
425
#endif
426
}
427
 
428
 
429
 
430
/*
6 7u83 431
 * FIND THE DATE STAMP OF A FILE
432
 *
433
 * This routine calculates the date stamp of a file. If the target machine
434
 * does not support stat, or this is a dry run, zero is always returned.
435
 */
2 7u83 436
 
6 7u83 437
static long
438
file_time(char *nm)
2 7u83 439
{
440
#if FS_STAT
6 7u83 441
	{
442
		int e;
443
		struct stat st;
444
		if (dry_run) {
445
			return (0);
446
		}
447
		e = stat(nm, &st);
448
		if (e == -1) {
449
			error(SERIOUS, "Can't access file '%s'", nm);
450
			return (0);
451
		}
452
		return ((long)st.st_mtime);
2 7u83 453
	}
454
#else
6 7u83 455
	{
456
		UNUSED(nm);
457
		return (0);
458
	}
2 7u83 459
#endif
460
}
461
 
462
 
463
/*
6 7u83 464
 * ARCHIVE HEADER
465
 *
466
 * A TDF archive always starts with ARCHIVE_HEADER, and the main part of the
467
 * archive ends with ARCHIVE_TRAILER.
468
 */
2 7u83 469
 
470
#define ARCHIVE_HEADER		"!TDF\n"
471
#define ARCHIVE_TRAILER		"-\n"
472
 
473
 
474
/*
6 7u83 475
 * IS A FILE AN ARCHIVE?
476
 *
477
 * This routine returns 1 if the file named nm starts with ARCHIVE_HEADER (and
478
 * so is probably an archive), and 0 otherwise.
479
 */
2 7u83 480
 
6 7u83 481
boolean
482
is_archive(char *nm)
2 7u83 483
{
6 7u83 484
	boolean b = 0;
485
	FILE *f = fopen(nm, "rb");
486
	if (f == null) {
487
		return (b);
488
	}
489
	if (fgets(buffer, 20, f) && streq(buffer, ARCHIVE_HEADER)) {
490
		b = 1;
491
	}
492
	IGNORE fclose(f);
493
	return (b);
2 7u83 494
}
495
 
496
 
497
/*
6 7u83 498
 * ARCHIVE FLAGS
499
 *
500
 * These flags control the output of the file names and options in the output
501
 * TDF archive.
502
 */
2 7u83 503
 
6 7u83 504
int archive_type = TDF_ARCHIVE;
505
static boolean archive_full = 1;
506
static boolean archive_links = 0;
507
static boolean archive_names = 1;
508
static boolean archive_options = 1;
2 7u83 509
 
510
 
511
/*
6 7u83 512
 * PROCESS ARCHIVE OPTIONS
513
 *
514
 * This routine processes any outstanding archive options.
515
 */
2 7u83 516
 
6 7u83 517
void
518
process_archive_opt(void)
2 7u83 519
{
6 7u83 520
	list *p;
521
	for (p = opt_joiner; p != null; p = p->next) {
522
		char *opt = p->item;
523
		if (streq(opt, "-copy") || streq(opt, "-c")) {
524
			archive_links = 0;
525
			link_specs = 0;
526
		} else if (streq(opt, "-full") || streq(opt, "-f")) {
527
			archive_full = 1;
528
		} else if (streq(opt, "-link") || streq(opt, "-l")) {
529
			archive_links = 1;
530
			link_specs = 1;
531
		} else if (streq(opt, "-names") || streq(opt, "-n")) {
532
			archive_names = 1;
533
		} else if (streq(opt, "-no_names") || streq(opt, "-nn")) {
534
			archive_names = 0;
535
		} else if (streq(opt, "-no_options") || streq(opt, "-no")) {
536
			archive_options = 0;
537
		} else if (streq(opt, "-options") || streq(opt, "-o")) {
538
			archive_options = 1;
539
		} else if (streq(opt, "-short") || streq(opt, "-s")) {
540
			archive_full = 0;
541
		} else {
542
			error(WARNING, "Unknown archiver option, '%s'", opt);
543
		}
2 7u83 544
	}
6 7u83 545
	opt_joiner = null;
546
	return;
2 7u83 547
}
548
 
549
 
550
/*
6 7u83 551
 * BUILD AN ARCHIVE
552
 *
553
 * This routine creates a TDF archive called arch from the null-terminated list
554
 * of files and options, input. The string ARCHIVE_OPTION_START is uses to
555
 * indicate the end of the files and the beginning of the options. The routine
556
 * returns zero if it is successful.
557
 */
2 7u83 558
 
6 7u83 559
int
560
build_archive(char *arch, char **input)
2 7u83 561
{
6 7u83 562
    FILE *f;
563
    char **s;
564
    boolean end = 0;
565
    if (dry_run) {
566
	    return (0);
2 7u83 567
    }
6 7u83 568
    f = fopen(arch, "wb");
569
    if (f == null) {
570
	error(SERIOUS, "Can't open output archive, '%s'", arch);
571
	return (1);
572
    }
573
    IGNORE fputs(ARCHIVE_HEADER, f);
574
    for (s = input; *s; s++) {
575
	if (end) {
2 7u83 576
	    /* Archive options */
6 7u83 577
	    if (archive_options) {
578
		if (verbose) {
579
		    comment(1, "... archive option %s\n", *s);
2 7u83 580
		}
6 7u83 581
		IGNORE fprintf(f, "%s\n", *s);
2 7u83 582
	    }
6 7u83 583
	} else if (streq(*s, ARCHIVE_OPTION_START)) {
2 7u83 584
	    /* Start of archive options */
6 7u83 585
	    IGNORE fputs(ARCHIVE_TRAILER, f);
586
	    end = 1;
587
	} else if (archive_links && archive_type != TDF_ARCHIVE) {
2 7u83 588
	    /* Archive file - link */
6 7u83 589
	    char *ln = *s;
590
	    if (verbose) {
591
		comment(1, "... archive file %s (link)\n", ln);
2 7u83 592
	    }
6 7u83 593
	    if (archive_full) {
594
		    ln = find_fullname(ln);
595
	    }
596
	    IGNORE fprintf(f, "> %ld %s\n", file_time(ln), ln);
2 7u83 597
	} else {
598
	    /* Archive file - copy */
6 7u83 599
	    FILE *g;
600
	    char *n = find_basename(*s);
601
	    if (!archive_names) {
602
		int i, m = (int)strlen(n);
603
		buffer [0] = '*';
604
		buffer [1] = 0;
605
		for (i = m - 1; i >= 0; i--) {
606
		    if (n [i] == '.') {
607
			IGNORE strcpy(buffer + 1, n + i);
608
			break;
2 7u83 609
		    }
610
		}
6 7u83 611
		n = buffer;
2 7u83 612
	    }
6 7u83 613
	    if (verbose) {
614
		    comment(1, "... archive file %s\n", *s);
615
	    }
616
	    g = fopen(*s, "rb");
617
	    if (g == null) {
618
		error(SERIOUS, "Can't open '%s' for archiving", *s);
619
		IGNORE fclose(f);
620
		return (1);
2 7u83 621
	    } else {
6 7u83 622
		pointer p = (pointer)buffer;
623
		size_t m = fread(p, sizeof(char), (size_t)block_size, g);
624
		IGNORE fprintf(f, "+ %ld %s\n",(long)m, n);
625
		while (m) {
626
		    if (fwrite(p, sizeof(char), m, f)!= m) {
627
			error(SERIOUS, "Write error in archive '%s'", arch);
628
			IGNORE fclose(f);
629
			return (1);
2 7u83 630
		    }
6 7u83 631
		    m = fread(p, sizeof(char), (size_t)block_size, g);
632
		    if (m) {
633
			    IGNORE fprintf(f, "+ %ld +\n", (long)m);
634
		    }
2 7u83 635
		}
6 7u83 636
		IGNORE fclose(g);
2 7u83 637
	    }
638
	}
639
    }
6 7u83 640
    if (!end) {
641
	    IGNORE fputs(ARCHIVE_TRAILER, f);
642
    }
643
    IGNORE fclose(f);
644
    return (0);
2 7u83 645
}
646
 
647
 
648
/*
6 7u83 649
 * SPLIT AN ARCHIVE
650
 *
651
 * This routine splits the TDF archive named arch into it consistuent
652
 * components. Any files from the archive are stored in the location indicated
653
 * by ret. The routine returns zero if it is successful.
654
 */
2 7u83 655
 
6 7u83 656
int
657
split_archive(char *arch, filename **ret)
2 7u83 658
{
6 7u83 659
    boolean go = 1;
660
    char *emsg = null;
661
    list *opts = null;
662
    filename *q = null;
663
    filename *output = null;
664
    boolean need_moves = 0;
2 7u83 665
 
666
    /* Open archive file */
6 7u83 667
    FILE *f = fopen(arch, "rb");
668
    if (f == null) {
669
	emsg = "Can't open input archive, '%s'";
670
	goto archive_error;
2 7u83 671
    }
672
 
673
    /* Check for archive header */
6 7u83 674
    if (fgets(buffer, buffer_size, f) == null ||
675
	 !streq(buffer, ARCHIVE_HEADER)) {
676
	emsg = "Illegal input archive, '%s'";
677
	goto archive_error;
2 7u83 678
    }
679
 
680
    /* Extract archived files */
681
    do {
6 7u83 682
	if (fgets(buffer, buffer_size, f) == null) {
683
	    emsg = "Premature end of archive '%s'";
684
	    goto archive_error;
2 7u83 685
	}
6 7u83 686
	if (buffer [0] == '+' && buffer [1] == ' ') {
2 7u83 687
	    /* Archived file - copy */
6 7u83 688
	    char c;
689
	    long n = 0;
690
	    char *w = "wb";
691
	    char *p = buffer + 2;
692
	    int m = (int)strlen(buffer) - 1;
693
	    if (buffer [m] == '\n') {
694
		    buffer [m] = 0;
695
	    }
696
	    while (c = *(p++), c != ' ') {
697
		if (c < '0' || c > '9') {
698
		    emsg = "Illegal file length specifier in archive '%s'";
699
		    goto archive_error;
2 7u83 700
		}
6 7u83 701
		n = 10 * n + (c - '0');
2 7u83 702
	    }
6 7u83 703
	    if (streq(p, "+")) {
2 7u83 704
		/* File continuations */
6 7u83 705
		if (q == null) {
706
		    emsg = "Illegal file continuation in archive '%s'";
707
		    goto archive_error;
2 7u83 708
		}
6 7u83 709
		w = "ab";
2 7u83 710
	    } else {
6 7u83 711
		filename *qo = q;
712
		if (streq(p, "*")) {
2 7u83 713
		    /* Old form hidden names */
6 7u83 714
		    int k = where(INDEP_TDF);
715
		    q = make_filename(no_filename, INDEP_TDF, k);
716
		} else if (strneq(p, "*.", 2)) {
2 7u83 717
		    /* New form hidden names */
6 7u83 718
		    int t;
719
		    p = string_copy(p);
720
		    q = find_filename(p, UNKNOWN_TYPE);
721
		    t = q->type;
722
		    q = make_filename(no_filename, t, where(t));
2 7u83 723
		} else {
724
		    /* Unhidden names */
6 7u83 725
		    p = string_copy(p);
726
		    q = find_filename(p, UNKNOWN_TYPE);
727
		    q = make_filename(q, q->type, where(q->type));
2 7u83 728
		}
6 7u83 729
		if (archive_type != TDF_ARCHIVE && qo) {
730
			q->uniq = qo->uniq;
2 7u83 731
		}
6 7u83 732
		if (q->type == archive_type && q->storage != TEMP_FILE) {
733
		    filename *qn = make_filename(q, q->type, TEMP_FILE);
734
		    qn->aux = q;
735
		    qn->uniq = q->uniq;
736
		    q = qn;
737
		    need_moves = 1;
2 7u83 738
		}
6 7u83 739
		output = add_filename(output, q);
740
		if (verbose) {
741
		    comment(1, "... extract file %s\n", q->name);
742
		}
2 7u83 743
	    }
6 7u83 744
	    if (read_file(q->name, w, n, f)) {
745
		emsg = "Read error in archive '%s'";
746
		goto archive_error;
2 7u83 747
	    }
6 7u83 748
	} else if (buffer [0] == '>' && buffer [1] == ' ') {
2 7u83 749
	    /* Archived file - link */
6 7u83 750
	    char c;
751
	    long ad = 0, fd;
752
	    filename *qo = q;
753
	    char *p = buffer + 2;
754
	    int m = (int)strlen(buffer) - 1;
755
	    if (buffer [m] == '\n') {
756
		    buffer [m] = 0;
757
	    }
758
	    while (c = *(p++), c != ' ') {
759
		if (c < '0' || c > '9') {
760
		    emsg = "Illegal link information in archive '%s'";
761
		    goto archive_error;
2 7u83 762
		}
6 7u83 763
		ad = 10 * ad + (c - '0');
2 7u83 764
	    }
6 7u83 765
	    q = find_filename(string_copy(p), UNKNOWN_TYPE);
766
	    q->storage = PRESERVED_FILE;
767
	    if (archive_type != TDF_ARCHIVE && qo) {
768
		    q->uniq = qo->uniq;
2 7u83 769
	    }
6 7u83 770
	    output = add_filename(output, q);
771
	    if (verbose) {
772
		comment(1, "... extract file %s (link)\n", q->name);
2 7u83 773
	    }
6 7u83 774
	    fd = file_time(q->name);
775
	    if (ad && fd && ad != fd) {
776
		error(WARNING, "Date stamp on file '%s' has changed",
777
			q->name);
778
	    }
779
	} else if (streq(buffer, ARCHIVE_TRAILER)) {
2 7u83 780
	    /* Archived options */
6 7u83 781
	    char *p;
782
	    int c, m;
783
	    while (c = getc(f), c != EOF) {
784
		buffer [0] = (char)c;
785
		if (fgets(buffer + 1, buffer_size - 1, f) == null) {
786
		    emsg = "Premature end of archive '%s'";
787
		    goto archive_error;
2 7u83 788
		}
6 7u83 789
		m = (int)strlen(buffer) - 1;
790
		if (buffer [m] == '\n') {
791
			buffer [m] = 0;
792
		}
793
		p = string_copy(buffer);
794
		if (verbose) {
795
			comment(1, "... extract option %s\n", p);
796
		}
797
		opts = add_item(opts, p);
2 7u83 798
	    }
6 7u83 799
	    go = 0;
2 7u83 800
	} else {
6 7u83 801
	    emsg = "Illegal file description in archive '%s'";
802
	    goto archive_error;
2 7u83 803
	}
6 7u83 804
    } while (go);
2 7u83 805
 
806
    /* Return */
6 7u83 807
archive_error:
808
    if (emsg) {
809
	    error(SERIOUS, emsg, arch);
810
    }
811
    IGNORE fclose(f);
812
    if (need_moves) {
813
	    for (q = output; q != null; q = q->next) {
814
		    if (q->aux && keeps_aux [ archive_type ]) {
815
			    if (verbose) {
816
				    comment(1, "... rename %s to %s\n",
817
					    q->name, q->aux->name);
818
			    }
819
			    if (move_file(q->name, q->aux->name)) {
820
				    emsg = "rhubarb";
821
			    } else {
822
				    q->name = q->aux->name;
823
				    q->storage = q->aux->storage;
824
			    }
2 7u83 825
		    }
6 7u83 826
		    q->aux = null;
2 7u83 827
	    }
828
    }
6 7u83 829
    *ret = output;
830
    if (opts) {
831
	    process_options(opts, main_optmap, 0);
832
	    opt_archive = add_list(opt_archive, opts);
833
    }
834
    if (emsg) {
835
	    return (1);
836
    }
837
    return (0);
2 7u83 838
}