Subversion Repositories tendra.SVN

Rev

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

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