1 /*
2 par.c -- Quake *.pak file archiver
3 Copyright (C) 1998-2004 Steffen Solyga <solyga@absinth.net>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 /*
21 * $Id: par.c,v 1.3 2004/08/05 22:57:07 solyga Exp $
22 */
23
24
25 #include "header.h"
26
27
28 int
display_help(char * pn)29 display_help( char* pn ) {
30 fprintf( HELP_CHANNEL, "%s v%s (%s): ", pn, VERSION_NUMBER, DATE_OF_LAST_MOD );
31 fprintf( HELP_CHANNEL, "Quake Pak ARchiving utility.\n" );
32 fprintf( HELP_CHANNEL, "Flowers & bug reports to %s.\n", MY_EMAIL_ADDRESS );
33 fprintf( HELP_CHANNEL, "Usage: %s options archive [files]\n", pn );
34 fprintf( HELP_CHANNEL, "switches:\n" );
35 fprintf( HELP_CHANNEL, " -c\t create new pak archive\n" );
36 fprintf( HELP_CHANNEL, " -f\t force actions\n" );
37 fprintf( HELP_CHANNEL, " -h\t write this info to %s and exit sucessfully\n",HELP_CHANNEL==stdout?"stdout":"stderr" );
38 fprintf( HELP_CHANNEL, " -l\t list contents of pak archive\n" );
39 fprintf( HELP_CHANNEL, " -t\t files !!contain!! the names of the files to process\n" );
40 fprintf( HELP_CHANNEL, " -v\t be verbose\n" );
41 fprintf( HELP_CHANNEL, " -x\t extract files from archive\n" );
42 fprintf( HELP_CHANNEL, " -V\t print version and compilation info to %s and exit sucessfully\n", VERSION_CHANNEL==stdout?"stdout":"stderr" );
43 fprintf( HELP_CHANNEL, "Hint: %s doesn't work with stdin/stdout.\n", pn );
44 return( 0 );
45 }
46
47
48 int
display_version(char * pn)49 display_version( char* pn ) {
50 fprintf( VERSION_CHANNEL, "%s v%s (%s)\n", pn, VERSION_NUMBER, DATE_OF_LAST_MOD );
51 return( 0 );
52 }
53
54
is_in_list(char * list,char * name)55 int is_in_list(
56 char* list,
57 char* name )
58 /* returns index+1 of name in list or 0 if name is not in list */
59 /* if name is empty, number_of_entries+1 is returned */
60 {
61 char *pl= list;
62 char *pn;
63 int index= 0; /* index in [0,noe) */
64 while( *pl != '\0' )
65 {
66 for( pn=name; ; pl++,pn++ )
67 {
68 if( *pl != *pn ) break;
69 if( *pl == '\0' ) return( index+1 );
70 }
71 while( *pl++ != '\0' ); /* next entry */
72 index++;
73 }
74 return( *name=='\0' ? index+1 : 0 );
75 }
76
77
add_to_list(char * name)78 char* add_to_list(
79 char* name )
80 {
81 static long nba= 0; /* number of bytes allocated */
82 static char *p0=NULL; /* memory start */
83 static char *p1=NULL; /* write position */
84 char *p= name; /* tmp pointer */
85 int len= 1; /* name length including '\0' */
86 while( *p++ != '\0' ) len++;
87 if( p1-p0+len >= nba )
88 {
89 unsigned long diff= p1-p0;
90 nba+= MALLOC_SIZE;
91 if( (p1=realloc(p0,nba)) == NULL ) return( realloc(p0,0) );
92 p0= p1; p1+= diff;
93 *p1= '\0'; /* init for first call */
94 #ifdef DEBUG
95 fprintf( DEBUG_CHANNEL, "DEBUG: add_to_list(): Allocated %#08x for list.\n", p0 );
96 #endif
97 }
98 /* check name */
99 if( is_in_list(p0,name) ) return( p0 );
100 /* append name */
101 #ifdef DEBUG
102 fprintf( DEBUG_CHANNEL, "DEBUG: add_to_list(): Adding `%s' to list at %#08x.\n", name, p1 );
103 #endif
104 for( p=name; (*p1++=*p++)!='\0'; );
105 *p1= '\0'; /* end-of-list code */
106 return( p0 );
107 }
108
109
list_entry(char * list,int index)110 char* list_entry(
111 char* list,
112 int index )
113 /* returns pointer to index-th entry, index in [0,noe) */
114 /* if index is out of bounds, string of size 0 is returned */
115 {
116 char* p= list;
117 int i= 0;
118 if( list == NULL ) return( NULL );
119 while( *p != '\0' && i<index )
120 {
121 while( *p++ != '\0' );
122 i++;
123 }
124 return( p );
125 }
126
127
UCHARs_2_uint32_t(unsigned char * p)128 uint32_t UCHARs_2_uint32_t(
129 unsigned char *p )
130 {
131 uint32_t val= (uint32_t)0;
132 int i;
133 for( i=0; i<sizeof(uint32_t); i++ ) val|= p[i]<<(i*8);
134 return( val );
135 }
136
137
uint32_t_2_UCHARs(uint32_t val,unsigned char * p)138 uint32_t uint32_t_2_UCHARs(
139 uint32_t val,
140 unsigned char* p )
141 {
142 int i;
143 for( i=0; i<sizeof(uint32_t); i++) p[i]= (unsigned char)(val>>(i*8)&0xff);
144 return( val );
145 }
146
147
my_open(char * fn,int mode,mode_t perm,char * pn)148 int my_open(
149 char* fn, /* file name */
150 int mode, /* access mode (flags for open(2)) */
151 mode_t perm, /* permissions (mode for open(2)) */
152 char* pn ) /* name of main program */
153 /* returns file descriptor or -1 on error */
154 {
155 int fd;
156 if( (fd=open(fn,mode,perm)) == -1 )
157 fprintf(ERROR_CHANNEL,"%s: Cannot open `%s' for %s. %s.\n",
158 pn, fn, FILE_ACCESS_MODE(mode), strerror(errno));
159 return( fd );
160 }
161
162
my_close(int fd,char * fn,char * pn)163 int my_close(
164 int fd, /* file descriptor */
165 char* fn, /* file name */
166 char* pn ) /* name of main program */
167 /* returns 0 or -1 on error */
168 {
169 int rv;
170 if( (rv=close(fd)) == -1 )
171 fprintf(ERROR_CHANNEL,"%s: Could not close `%s' successfully. %s.\n",
172 pn, fn, strerror(errno));
173 return( rv );
174 }
175
176
my_lseek(int fd,uint32_t off,int whence,char * fn,char * pn)177 uint32_t my_lseek(
178 int fd, /* file descriptor */
179 uint32_t off, /* file offset */
180 int whence, /* reference position */
181 char* fn, /* file name */
182 char* pn ) /* name of main program */
183 /* returns new file offset or -1 on error */
184 {
185 uint32_t new_off;
186 if( (new_off=lseek(fd,off,whence)) == -1 )
187 fprintf(ERROR_CHANNEL,"%s: Cannot lseek `%s'. %s.\n",
188 pn, fn, strerror(errno));
189 return( new_off );
190 }
191
192
my_read(int fd,void * buf,ssize_t nbtr,char * fn,char * pn)193 ssize_t my_read(
194 int fd, /* file descriptor */
195 void* buf, /* buffer */
196 ssize_t nbtr, /* number of bytes to read */
197 char* fn, /* file name */
198 char* pn ) /* name of main program */
199 /* returns number of bytes read or -1 on error */
200 /* like read(2) but nbr<nbtr only if eof reached */
201 {
202 ssize_t nbr;
203 ssize_t tnbr= 0;
204 ssize_t rem= nbtr;
205 unsigned char *p= (unsigned char*)buf;
206 do
207 {
208 if( (nbr=read(fd,p+tnbr,rem)) == -1 )
209 {
210 fprintf(ERROR_CHANNEL,"%s: Cannot read from `%s'. %s.\n",
211 pn, fn, strerror(errno));
212 return( -1 );
213 }
214 tnbr+= nbr;
215 rem= nbtr - tnbr;
216 }
217 while( nbr>0 && rem>0 );
218 return( tnbr );
219 }
220
221
my_write(int fd,void * buf,ssize_t nbtw,char * fn,char * pn)222 ssize_t my_write(
223 int fd, /* file descriptor */
224 void* buf, /* buffer */
225 ssize_t nbtw, /* number of bytes to write */
226 char* fn, /* file name */
227 char* pn ) /* name of main program */
228 /* writes nbtw buffer to fd */
229 /* returns number of bytes written ( ==nbtw ) or -1 on error */
230 {
231 ssize_t nbw;
232 ssize_t tnbw= 0;
233 ssize_t rem= nbtw;
234 unsigned char *p= (unsigned char*)buf;
235 do
236 {
237 if( (nbw=write(fd,p+tnbw,rem)) == -1 )
238 {
239 fprintf( ERROR_CHANNEL, "%s: Cannot write to `%s'. %s.\n",
240 pn, fn, strerror(errno));
241 return( -1 );
242 }
243 tnbw+= nbw;
244 rem= nbtw - tnbw;
245 }
246 while( nbw>0 && rem>0 );
247 if( tnbw < nbtw )
248 {
249 fprintf(ERROR_CHANNEL,"%s: Cannot write (%d bytes) to `%s'.\n",
250 pn, nbtw, fn );
251 return( -1 );
252 }
253 return( tnbw );
254 }
255
256
my_mkdir(char * fn,mode_t perm,char * pn)257 int my_mkdir(
258 char* fn, /* filename to use path from */
259 mode_t perm, /* permissions (mode for open(2)) */
260 char* pn ) /* name of main program */
261 /* returns 0 on success or -1 on error */
262 {
263 char buf[MAX_FN_LEN];
264 char *p;
265 int i,j;
266 int ndir= 0; /* number of directories to create */
267 for( p=fn; *p!='\0'; p++ ) if( *p=='/' ) ndir++;
268 for( j=0; j<ndir; j++ )
269 {
270 int cnt= 0;
271 for( i=0; cnt<=j; i++ )
272 if( (buf[i]=fn[i]) == '/' ) cnt++;
273 buf[i-1]= '\0';
274 if( (mkdir(buf,perm)==-1) && errno!=EEXIST )
275 {
276 fprintf( ERROR_CHANNEL, "%s: Cannot make directory `%s'. %s.\n",
277 pn, buf, strerror(errno) );
278 return( -1 );
279 }
280 }
281 return( 0 );
282 }
283
284
init_pak_header(struct pak_header * p_pak_hdr)285 int init_pak_header(
286 struct pak_header* p_pak_hdr ) /* pak header */
287 /* inits pak header structure (== pak header for empty archive) */
288 /* returns 0 on success or -1 on error */
289 {
290 if( p_pak_hdr == NULL ) return ( -1 );
291 strcpy( p_pak_hdr->magic, PAK_MAGIC );
292 p_pak_hdr->toc_off= (uint32_t) PAK_HDR_SIZE;
293 p_pak_hdr->toc_sze= (uint32_t) 0;
294 p_pak_hdr->pak_sze= (uint32_t) PAK_HDR_SIZE;
295 return( 0 );
296 }
297
298
read_pak_header(struct pak_header * p_pak_hdr,char * pak_fn,char * pn)299 int read_pak_header(
300 struct pak_header* p_pak_hdr, /* pak header */
301 char* pak_fn, /* pak file name */
302 char* pn ) /* name of main prg. */
303 /* reads header from pak archive */
304 /* returns 0 or -1 on error */
305 {
306 int rv= 0;
307 unsigned char buf[PAK_HDR_SIZE];
308 int i;
309 int pak_fd;
310 uint32_t pak_sze;
311 ssize_t nbr;
312 if( init_pak_header(p_pak_hdr) == -1 ) return( -1 );
313 if( (pak_fd=my_open(pak_fn,O_RDONLY,PERMISSIONS,pn)) == -1 ) return( -1 );
314 /* fill buffer */
315 if( (nbr=my_read(pak_fd,buf,PAK_HDR_SIZE,pak_fn,pn)) < PAK_HDR_SIZE )
316 { /* read error or file too small */
317 if( nbr > -1 )
318 fprintf( ERROR_CHANNEL, "%s: File `%s' is not a pak archive. File too small.\n", pn, pak_fn );
319 rv= -1; goto RETURN;
320 }
321 /* magic */
322 for( i=0; i<PAK_MAGIC_SIZE; i++ ) p_pak_hdr->magic[i]= buf[i];
323 /* toc offset */
324 p_pak_hdr->toc_off= UCHARs_2_uint32_t( buf+PAK_MAGIC_SIZE );
325 /* toc size */
326 p_pak_hdr->toc_sze= UCHARs_2_uint32_t( buf+PAK_MAGIC_SIZE+PAK_OFFSET_SIZE );
327 /* pak size */
328 if( (pak_sze=my_lseek(pak_fd,(uint32_t)0,SEEK_END,pak_fn,pn)) == -1 )
329 { rv= -1; goto RETURN; }
330 p_pak_hdr->pak_sze= pak_sze;
331 RETURN:
332 if( my_close(pak_fd,pak_fn,pn) == -1 ) return( -1 );
333 return( rv );
334 }
335
336
write_pak_header(struct pak_header * p_pak_hdr,int or_mode,char * pak_fn,char * pn)337 int write_pak_header(
338 struct pak_header* p_pak_hdr, /* pak header */
339 int or_mode, /* or-ed mode for open*/
340 char* pak_fn, /* pak file name */
341 char* pn ) /* name of main prog. */
342 /* overwrites header of pak archive with values from p_pak_hdr */
343 /* returns 0 on success or -1 on error */
344 {
345 int pak_fd;
346 int rv= 0;
347 unsigned char buf[PAK_HDR_SIZE];
348 int i;
349 if( p_pak_hdr == NULL ) return( -1 );
350 if( (pak_fd=my_open(pak_fn,O_CREAT|O_WRONLY|or_mode,PERMISSIONS,pn)) == -1 )
351 { rv= -1; return( rv ); }
352 /* magic */
353 for( i=0; i<PAK_MAGIC_SIZE; i++ ) buf[i]= p_pak_hdr->magic[i];
354 /* toc offset */
355 uint32_t_2_UCHARs( p_pak_hdr->toc_off, buf+PAK_MAGIC_SIZE );
356 /* toc size */
357 uint32_t_2_UCHARs( p_pak_hdr->toc_sze, buf+PAK_MAGIC_SIZE+PAK_OFFSET_SIZE );
358 if( my_write(pak_fd,buf,PAK_HDR_SIZE,pak_fn,pn) == -1 )
359 { rv= -1; goto RETURN; }
360 RETURN:
361 my_close( pak_fd, pak_fn, pn );
362 return( rv );
363 }
364
365
check_pak_header(struct pak_header * p_pak_hdr,char * pak_fn,char * pn)366 int check_pak_header(
367 struct pak_header* p_pak_hdr, /* pak header */
368 char* pak_fn, /* pak file name */
369 char* pn ) /* name of main prog. */
370 /* returns number of toc entries or -1 on error */
371 {
372 int noe;
373 if( strcmp(p_pak_hdr->magic,PAK_MAGIC) )
374 {
375 fprintf(ERROR_CHANNEL,"%s: File `%s' is not a pak archive. %s.\n",
376 pn, pak_fn, "Wrong magic number");
377 return( -1 );
378 }
379 if( p_pak_hdr->toc_off < (uint32_t)PAK_HDR_SIZE )
380 {
381 fprintf(ERROR_CHANNEL,"%s: Pak archive `%s' corrupted. Toc offset = %d.\n",
382 pn, pak_fn, (int)p_pak_hdr->toc_off);
383 return( -1 );
384 }
385 if( p_pak_hdr->toc_sze < (uint32_t)0 )
386 {
387 fprintf(ERROR_CHANNEL,"%s: Pak archive `%s' corrupted. Toc size = %d.\n",
388 pn, pak_fn, (int)p_pak_hdr->toc_sze);
389 return( -1 );
390 }
391 if( p_pak_hdr->pak_sze != p_pak_hdr->toc_off + p_pak_hdr->toc_sze )
392 {
393 fprintf(ERROR_CHANNEL,"%s: Pak archive `%s' corrupted. %s.\n",
394 pn, pak_fn, "Pak file size != toc offset + toc size");
395 return( -1 );
396 }
397 noe= p_pak_hdr->toc_sze/PAK_TOC_ENTRY_SIZE;
398 if( p_pak_hdr->toc_sze%PAK_TOC_ENTRY_SIZE )
399 {
400 fprintf(ERROR_CHANNEL,"%s: Pak archive `%s' corrupted. %3.2f toc entries.\n", pn, pak_fn, (p_pak_hdr->toc_sze*1.0)/PAK_TOC_ENTRY_SIZE );
401 return( -1 );
402 }
403 #ifndef ALLOW_EMPTY_ARCHIVES
404 if( noe == 0 )
405 {
406 fprintf(ERROR_CHANNEL,"%s: Pak archive `%s' is empty.\n",
407 pn, pak_fn );
408 return( -1 );
409 }
410 #endif
411 return( noe );
412 }
413
414
realloc_pak_toc(struct pak_tocentry * pak_toc,int noe,char * pn)415 struct pak_tocentry* realloc_pak_toc(
416 struct pak_tocentry* pak_toc, /* pak toc */
417 int noe, /* number of entries to alloc */
418 char* pn ) /* name of main program */
419 /* reallocates memory for array[noe] of pak tocentries */
420 /* new memory is initialized */
421 /* returns pointer to tocentry-array or NULL on error or noe==0 */
422 /* on error the memory is freed */
423 {
424 static int noea= 0; /* number of entries already allocated for */
425 struct pak_tocentry* rv; /* return value */
426 int i,imax,j;
427 /*
428 The following if-statement is neccessary only due to a bug (with respect
429 to the man pages) in realloc(2) which doesn't return NULL if size is zero
430 */
431 if( noe )
432 {
433 if( (rv=realloc(pak_toc,(size_t)(noe*sizeof(struct pak_tocentry)))) == NULL )
434 {
435 fprintf( ERROR_CHANNEL,"%s: Allocation problems (%d bytes).\n",
436 pn, noe*sizeof(struct pak_tocentry) );
437 free( pak_toc );
438 return( NULL );
439 }
440 }
441 else
442 rv= NULL;
443 /* init new memory */
444 imax= sizeof(rv[0].f_nme);
445 for( j=noea; j<noe; j++ )
446 {
447 for( i=0; i<imax; i++ ) rv[j].f_nme[i]= '\0';
448 rv[j].f_off= (uint32_t)0;
449 rv[j].f_sze= (uint32_t)0;
450 }
451 noea= noe;
452 return( rv );
453 }
454
455
read_pak_toc(struct pak_header * p_pak_hdr,char * pak_fn,char * pn)456 struct pak_tocentry* read_pak_toc(
457 struct pak_header* p_pak_hdr, /* pak header */
458 char* pak_fn, /* pak file name */
459 char* pn ) /* name of main program */
460 /* returns address of first tocentry or NULL on error or empty pak */
461 {
462 struct pak_tocentry* pak_toc;
463 struct pak_tocentry* rv;
464 int noe= p_pak_hdr->toc_sze/PAK_TOC_ENTRY_SIZE; /* number of entries */
465 unsigned char buf[PAK_TOC_ENTRY_SIZE];
466 int pak_fd;
467 ssize_t nbr;
468 int i,j;
469 if( (pak_toc=realloc_pak_toc(NULL,noe,pn)) == NULL )
470 { return( NULL ); }
471 rv= pak_toc;
472 if( (pak_fd=my_open(pak_fn,O_RDONLY,PERMISSIONS,pn)) == -1 )
473 { rv= NULL; goto RETURN; }
474 if( my_lseek(pak_fd,p_pak_hdr->toc_off,SEEK_SET,pak_fn,pn) == -1 )
475 { rv= NULL; goto RETURN; }
476 /* fill entries */
477 for( j=0; j<noe; j++ )
478 {
479 if( (nbr=my_read(pak_fd,buf,PAK_TOC_ENTRY_SIZE,pak_fn,pn)) < PAK_TOC_ENTRY_SIZE )
480 { /* shouldn't happen if header has been checked against pak size */
481 if( nbr > -1 )
482 fprintf( ERROR_CHANNEL, "%s: Pak archive `%s' corrupted. %s.\n",
483 pn, pak_fn, "File too small" );
484 rv= NULL; goto RETURN;
485 }
486 for( i=0; i<PAK_TOC_FN_LEN; i++ ) pak_toc[j].f_nme[i]= buf[i];
487 pak_toc[j].f_off= UCHARs_2_uint32_t( buf+PAK_TOC_FN_LEN );
488 pak_toc[j].f_sze= UCHARs_2_uint32_t( buf+PAK_TOC_FN_LEN+PAK_OFFSET_SIZE );
489 }
490 RETURN:
491 if( rv == NULL ) realloc_pak_toc( pak_toc, 0, pn );
492 if( my_close(pak_fd,pak_fn,pn) == -1 ) return( NULL );
493 return( rv );
494 }
495
496
write_pak_toc(struct pak_header * p_pak_hdr,struct pak_tocentry * pak_toc,char * pak_fn,char * pn)497 int write_pak_toc(
498 struct pak_header* p_pak_hdr, /* pak header */
499 struct pak_tocentry* pak_toc, /* pak toc */
500 char* pak_fn, /* pak file name */
501 char* pn ) /* name of main program */
502 /* append toc to existing pak archive at p_pak_hdr->toc_off */
503 /* file header is not read */
504 /* returns 0 on success or -1 on error */
505 {
506 int pak_fd;
507 int pak_noe;
508 int rv= 0;
509 int i,j;
510 unsigned char buf[PAK_TOC_ENTRY_SIZE];
511 if( (pak_fd=my_open(pak_fn,O_WRONLY,PERMISSIONS,pn)) == -1 ) return( -1 );
512 if( my_lseek(pak_fd,p_pak_hdr->toc_off,SEEK_SET,pak_fn,pn) == -1 )
513 { rv= -1; goto RETURN; }
514 pak_noe= p_pak_hdr->toc_sze/PAK_TOC_ENTRY_SIZE;
515 for( j=0; j<pak_noe; j++ )
516 {
517 /* fill buffer */
518 for( i=0; i<PAK_TOC_FN_LEN; i++ ) buf[i]= '\0';
519 strcpy( (char*)buf, pak_toc[j].f_nme );
520 uint32_t_2_UCHARs( pak_toc[j].f_off, buf+PAK_TOC_FN_LEN );
521 uint32_t_2_UCHARs( pak_toc[j].f_sze, buf+PAK_TOC_FN_LEN+PAK_OFFSET_SIZE );
522 /* write buffer to pak file */
523 if( my_write(pak_fd,buf,PAK_TOC_ENTRY_SIZE,pak_fn,pn) == -1 )
524 { rv= -1; goto RETURN; }
525 }
526 RETURN:
527 if( my_close(pak_fd,pak_fn,pn) == -1 ) rv= -1;
528 return( rv );
529 }
530
531
check_pak_toc(struct pak_tocentry * pak_toc,int noe,char * pak_fn,char * pn)532 int check_pak_toc(
533 struct pak_tocentry* pak_toc, /* pak toc */
534 int noe, /* number of entries */
535 char* pak_fn, /* pak filename */
536 char* pn ) /* name of main program */
537 /* returns 0 on success or -1 on error */
538 {
539 int j;
540 if( pak_toc == NULL ) return( 0 ); /* emtpy toc */
541 if( pak_toc[0].f_off != PAK_HDR_SIZE )
542 {
543 fprintf(ERROR_CHANNEL,"%s: Pak archive `%s' corrupted (toc entry %d).\n",
544 pn, pak_fn, 0 );
545 return( -1 );
546 }
547 for( j=1; j<noe; j++ )
548 if( pak_toc[j].f_off < pak_toc[j-1].f_off + pak_toc[j-1].f_sze )
549 {
550 fprintf(ERROR_CHANNEL,"%s: Pak archive `%s' corrupted (toc entry %d).\n",
551 pn, pak_fn, j );
552 return( -1 );
553 }
554 return( 0 );
555 }
556
557
list_pak_toc(struct pak_tocentry * pak_toc,int pak_noe,char * list,int verbose,int force,char * pak_fn,char * pn)558 int list_pak_toc(
559 struct pak_tocentry* pak_toc, /* pak toc */
560 int pak_noe, /* number of entries */
561 char* list, /* file name list */
562 int verbose, /* verbose flag */
563 int force, /* force flag */
564 char* pak_fn, /* pak filename */
565 char* pn ) /* name of main program */
566 /* returns number of files listed or -1 on error */
567 {
568 int list_noe;
569 unsigned char* list_mark= NULL;
570 int maxlen= 0;
571 int i,j;
572 char* fn;
573 int nbl= 0; /* number of bytes listed */
574 int nfl= 0; /* number of files listed */
575 int tnb= 0; /* total number of bytes */
576 if( (list_noe=is_in_list(list,"")-1) )
577 {
578 if( (list_mark=malloc((list_noe)*sizeof(unsigned char))) == NULL )
579 {
580 fprintf( ERROR_CHANNEL, "%s: Allocation problem (%d bytes).\n",
581 pn, list_noe*sizeof(unsigned char) );
582 return( -1 );
583 }
584 for( j=0; j<list_noe; j++ ) list_mark[j]= (unsigned char)0;
585 }
586 /* find length of longest filename */
587 for( j=0; j<pak_noe; j++ )
588 {
589 fn= pak_toc[j].f_nme;
590 if( list_noe==0 || is_in_list(list,fn) )
591 maxlen= strlen(fn) > maxlen ? strlen(fn) : maxlen;
592 }
593 /* list */
594 for( j=0; j<pak_noe; j++ )
595 {
596 fn= pak_toc[j].f_nme;
597 tnb+= pak_toc[j].f_sze;
598 if( list_noe==0 || is_in_list(list,fn) )
599 {
600 nfl++;
601 nbl+= pak_toc[j].f_sze;
602 if( list_noe ) list_mark[is_in_list(list,fn)-1]= (unsigned char)1;
603 printf( "%s", fn );
604 if( verbose )
605 {
606 for( i=strlen(fn); i<maxlen; printf(" "), i++ );
607 printf( "%8d", (int)pak_toc[j].f_sze );
608 }
609 printf( "\n" );
610 }
611 }
612 if( list_noe && !force )
613 { /* check for specified but unlisted files */
614 for( j=0; j<list_noe; j++)
615 if( !list_mark[j] )
616 fprintf( ERROR_CHANNEL, "%s: File `%s' not found in archive.\n",
617 pn, list_entry(list,j) );
618 }
619 if( verbose )
620 {
621 printf( "Summary for pak archive `%s': \n", pak_fn );
622 printf( " Listed: %d file%s, %d bytes\n",
623 nfl, nfl>1?"s":"",nbl );
624 printf( " Total: %d file%s, %d bytes\n",
625 pak_noe, pak_noe>1?"s":"",tnb );
626 }
627 free( list_mark );
628 if( nfl<list_noe && !force ) return( -1 );
629 return( nfl );
630 }
631
632
extract_pak(struct pak_tocentry * pak_toc,int pak_noe,char * list,int verbose,int force,char * pak_fn,char * pn)633 int extract_pak(
634 struct pak_tocentry* pak_toc, /* pak toc */
635 int pak_noe, /* number of entries */
636 char* list, /* file name list */
637 int verbose, /* verbose flag */
638 int force, /* force flag */
639 char* pak_fn, /* pak filename */
640 char* pn ) /* name of main program */
641 /* returns number of files extracted or -1 on error */
642 {
643 int list_noe;
644 unsigned char* list_mark= NULL;
645 int nbe= 0; /* number of bytes extracted */
646 int nfe= 0; /* number of files extracted */
647 int tnb= 0; /* total number of bytes */
648 int j;
649 int pak_fd;
650 char* fn; /* filename from pak-toc == name of generated file */
651 int fd;
652 uint32_t off;
653 uint32_t sze;
654
655 if( (list_noe=is_in_list(list,"")-1) )
656 { /* set up marker array and init */
657 if( (list_mark=malloc((list_noe)*sizeof(unsigned char))) == NULL )
658 {
659 fprintf( ERROR_CHANNEL, "%s: Allocation problem (%d bytes).\n",
660 pn, list_noe*sizeof(unsigned char) );
661 return( -1 );
662 }
663 for( j=0; j<list_noe; j++ ) list_mark[j]= (unsigned char)0;
664 }
665 /* extract */
666 if( (pak_fd=my_open(pak_fn,O_RDONLY,PERMISSIONS,pn)) == -1 )
667 { nfe= -1; goto RETURN; }
668 for( j=0; j<pak_noe; j++ )
669 {
670 fn= pak_toc[j].f_nme;
671 off= pak_toc[j].f_off;
672 sze= pak_toc[j].f_sze;
673 tnb+= sze;
674 if( list_noe==0 || is_in_list(list,fn) )
675 { /* extract one file */
676 unsigned char buf[CP_BUF_SIZE]; /* buffer for copying */
677 ssize_t rem= sze; /* remaining bytes */
678 ssize_t nbr;
679 ssize_t nbtr;
680 if( verbose ) printf( "%s, %d bytes\n", fn, (int)sze );
681 if( my_lseek( pak_fd, off, SEEK_SET, pak_fn, pn ) == -1 )
682 { nfe= -1; goto RETURN; }
683 if( my_mkdir(fn,DIR_PERMISSIONS,pn) == -1 )
684 { nfe= -1; goto RETURN; }
685 if( (fd=my_open(fn,O_CREAT|O_WRONLY|O_TRUNC,PERMISSIONS,pn)) == -1 )
686 { nfe= -1; goto RETURN; }
687 while( rem > 0 )
688 {
689 nbtr= rem>CP_BUF_SIZE?CP_BUF_SIZE:rem;
690 if( (nbr=my_read(pak_fd,buf,nbtr,fn,pn)) < nbtr )
691 { /* shouldn't happen if pak-toc has been checked against pak size */
692 if( nbr > -1 )
693 fprintf( ERROR_CHANNEL, "%s: Pak archive `%s' corrupted. %s.\n",
694 pn, pak_fn, "File too small." );
695 nfe= -1; my_close(fd,fn,pn); goto RETURN;
696 }
697 if( my_write(fd,buf,nbr,fn,pn) == -1 )
698 { nfe= -1; my_close(fd,fn,pn); goto RETURN; }
699 rem-= nbr;
700 }
701 if( my_close(fd,fn,pn) == -1 )
702 { nfe= -1; goto RETURN; }
703 if( list_noe ) list_mark[is_in_list(list,fn)-1]= (unsigned char)1;
704 nfe++;
705 nbe+= sze;
706 }
707 }
708 if( list_noe && !force )
709 { /* check for specified but unextracted files */
710 for( j=0; j<list_noe; j++)
711 if( !list_mark[j] )
712 fprintf( ERROR_CHANNEL, "%s: File `%s' not found in archive.\n",
713 pn, list_entry(list,j) );
714 }
715 if( verbose )
716 {
717 printf( "Summary for pak archive `%s': \n", pak_fn );
718 printf( " Extracted: %d file%s, %d bytes\n",
719 nfe, nfe>1?"s":"", nbe );
720 printf( " Total: %d file%s, %d bytes\n",
721 pak_noe, pak_noe>1?"s":"", tnb );
722 }
723 RETURN:
724 free( list_mark );
725 my_close( pak_fd, pak_fn, pn );
726 if( nfe<list_noe && !force ) return( -1 );
727 return( nfe );
728 }
729
730
write_pak(struct pak_header * p_pak_hdr,char * list,int verbose,int force,char * pak_fn,char * pn)731 struct pak_tocentry* write_pak(
732 struct pak_header* p_pak_hdr, /* pak header */
733 char* list, /* file (name) list */
734 int verbose, /* be talketive */
735 int force, /* ignore some errors */
736 char* pak_fn, /* pak filename */
737 char* pn ) /* name of main program */
738 /* writes files from list to pak archive at PAK_HDR_SIZE */
739 /* sets up pak_toc and updates p_pak_hdr */
740 /* neither pak_toc nor p_pak_hdr is written */
741 /* archive MUST exist and MUST be smaller than the resulting one !!!! */
742 /* returns pac_toc on success or NULL on error or empty list */
743 {
744 struct pak_tocentry* pak_toc;
745 struct pak_tocentry* rv;
746 int pak_fd;
747 uint32_t pak_off;
748 int pak_noe= 0;
749 char* fn;
750 int fd;
751 uint32_t sze;
752 int j;
753 int list_noe= is_in_list(list,"")-1;
754 unsigned char buf[CP_BUF_SIZE]; /* copy buffer */
755 ssize_t rem; /* remaining bytes */
756 ssize_t nbr; /* number of bytes read */
757 ssize_t nbtr; /* number of bytes to read */
758 p_pak_hdr->toc_sze= list_noe*PAK_TOC_ENTRY_SIZE; /* for error detection */
759 if( (pak_toc=realloc_pak_toc(NULL,list_noe,pn)) == NULL ) return( NULL );
760 /* non-empty list */
761 rv= pak_toc;
762 if( (pak_fd=my_open(pak_fn,O_WRONLY,PERMISSIONS,pn)) == -1 )
763 { rv= NULL; goto RETURN; }
764 if( (pak_off=my_lseek(pak_fd,PAK_HDR_SIZE,SEEK_SET,pak_fn,pn)) == -1 )
765 { rv= NULL; goto RETURN; }
766 for( j=0; j<list_noe; j++ )
767 {
768 fn= list_entry( list, j );
769 #ifdef DEBUG
770 fprintf( DEBUG_CHANNEL,"DEBUG: write_pak(): Processing `%s' (list index %d).\n", fn, j );
771 #endif
772 if( strlen(fn)+1 > MAX_FN_LEN )
773 { /* name too long */
774 if( force )
775 {
776 p_pak_hdr->toc_sze-= PAK_TOC_ENTRY_SIZE;
777 continue;
778 }
779 rv= NULL; goto RETURN;
780 }
781 if( (fd=open(fn,O_RDONLY)) == -1 )
782 {
783 if( force )
784 {
785 p_pak_hdr->toc_sze-= PAK_TOC_ENTRY_SIZE;
786 continue;
787 }
788 my_open(fn,O_RDONLY,PERMISSIONS,pn); /* for error message only */
789 rv= NULL; goto RETURN;
790 }
791 if( (sze=my_lseek(fd,(uint32_t)0,SEEK_END,fn,pn)) == -1 )
792 { rv= NULL; my_close(fd,fn,pn); goto RETURN; }
793 my_lseek(fd,(uint32_t)0,SEEK_SET,fn,pn);
794 /* copy data of one file */
795 #ifdef DEBUG
796 fprintf( DEBUG_CHANNEL,"DEBUG: write_pak(): Copying file `%s' to pak file @ %#08x\n", fn, pak_off );
797 #endif
798 if( verbose ) printf( "%s, %d bytes\n", fn, (int)sze );
799 rem= sze;
800 while( rem > 0 )
801 {
802 nbtr= rem>CP_BUF_SIZE?CP_BUF_SIZE:rem;
803 if( (nbr=my_read(fd,buf,nbtr,fn,pn)) < nbtr )
804 { /* shouldn't happen since size is known from lseek */
805 if( nbr > -1 )
806 fprintf( ERROR_CHANNEL, "%s: Cannot read from file `%s'. %s.\n",
807 pn, fn, "File too small" );
808 rv= NULL; my_close(fd,fn,pn); goto RETURN;
809 }
810 if( my_write(pak_fd,buf,nbr,pak_fn,pn) == -1 )
811 { rv= NULL; my_close(fd,fn,pn); goto RETURN; }
812 rem-= nbr;
813 }
814 /* set toc data */
815 strcpy( pak_toc[pak_noe].f_nme, fn );
816 pak_toc[pak_noe].f_off= pak_off;
817 pak_toc[pak_noe].f_sze= sze;
818 pak_noe++;
819 pak_off+= sze;
820 if( my_close(fd,fn,pn) == -1 )
821 { rv= NULL; goto RETURN; }
822 }
823 /* update p_pak_hdr */
824 p_pak_hdr->toc_off= pak_off;
825 p_pak_hdr->toc_sze= pak_noe*PAK_TOC_ENTRY_SIZE;
826 RETURN:
827 my_close( pak_fd, pak_fn, pn );
828 if( rv == NULL ) free(pak_toc);
829 return( rv );
830 }
831
832
833 char*
basename(char * name)834 basename( char* name ) {
835 /*
836 * strip directory from name
837 * returns pointer to stripped name
838 * hint: basename("/usr/bin/") == ""
839 * basename(1) would return "bin" !!
840 */
841 char* p= name;
842 while( *p != '\0' ) p++;
843 while( p > name ) {
844 if( *(p-1) == '/' ) break;
845 else p--;
846 }
847 return( p );
848 }
849
850
851
main(int argc,char ** argv)852 int main( int argc, char** argv) {
853 /*
854 * main() par
855 * started : Sat Jul 4 00:33:50 MET DST 1998 @beast
856 */
857
858 /*** getopt stuff ***/
859 int c;
860 extern char *optarg;
861 extern int optind;
862 /*** options stuff ***/
863 int action= ACTION_NONE;
864 int indirect_files= 0;
865 int force= 0;
866 int verbose= 0;
867 /*** file stuff ***/
868 char *pak_fn= NULL; /* pak file name */
869 int pak_fd= 0; /* pak file descriptor */
870 struct pak_header pak_hdr; /* pak file header */
871 int pak_nf; /* pak file number of files cantained */
872 struct pak_tocentry *pak_toc=NULL; /* pak file toc (array of tocentries) */
873 char *tmp_fn;
874 int tmp_fd= 0;
875 char fn_buf[MAX_FN_LEN];
876 int nbr;
877 /*** miscellanious ***/
878 char *file_list= add_to_list(""); /* init list */
879 int retval= RETVAL_OK;
880 char* fpn= *argv; /* full program name */
881 char* pn= basename( fpn ); /* prg_name is Makefile constant */
882 int i;
883 int index;
884 int eof_reached;
885 /***** process options *****/
886 *argv= pn; /* give getop() the cut name */
887 while( (c=getopt(argc,argv,"cfhltvxV")) != EOF )
888 switch( c ) {
889 case 'c': /* action: create */
890 if( action == ACTION_NONE ) action= ACTION_CREATE;
891 else {
892 fprintf( ERROR_CHANNEL,
893 "%s: Two actions specified. Try -h for help.\n", pn );
894 retval= RETVAL_ERROR; goto DIE_NOW;
895 }
896 break;
897 case 'f': /* force action */
898 force= 1;
899 break;
900 case 'h': /* display help to HELP_CHANNEL and exit sucessfully */
901 display_help( pn );
902 retval= RETVAL_OK; goto DIE_NOW;
903 case 'l': /* action: list */
904 if( action == ACTION_NONE ) action= ACTION_LIST;
905 else {
906 fprintf( ERROR_CHANNEL,
907 "%s: Two actions specified. Try -h for help.\n", pn );
908 retval= RETVAL_ERROR; goto DIE_NOW;
909 }
910 break;
911 case 't': /* get filenames from specified files */
912 indirect_files= 1;
913 break;
914 case 'v': /* be verbose */
915 verbose++;
916 break;
917 case 'x': /* action: extract */
918 if( action == ACTION_NONE ) action= ACTION_EXTRACT;
919 else {
920 fprintf( ERROR_CHANNEL,
921 "%s: Two actions specified. Try -h for help.\n", pn );
922 retval= RETVAL_ERROR; goto DIE_NOW;
923 }
924 break;
925 case 'V': /* display version to VERSION_CHANNEL and exit sucessfully */
926 display_version( pn );
927 retval= RETVAL_OK; goto DIE_NOW;
928 case '?': /* refer to -h and exit unsucessfully */
929 fprintf( ERROR_CHANNEL, "%s: Try `%s -h' for more information.\n",
930 pn, fpn );
931 retval= RETVAL_ERROR; goto DIE_NOW;
932 default : /* program error */
933 fprintf( ERROR_CHANNEL, "%s: Options bug! E-mail me at %s.\n",
934 pn, MY_EMAIL_ADDRESS );
935 retval= RETVAL_BUG; goto DIE_NOW;
936 }
937 if( action == ACTION_NONE ) {
938 fprintf( ERROR_CHANNEL, "%s: No action specified. Try -h for help.\n", pn );
939 retval= RETVAL_ERROR; goto DIE_NOW;
940 }
941 /***** set pak_fn *****/
942 if( optind == argc ) {
943 fprintf( ERROR_CHANNEL, "%s: No archive specified.\n", pn );
944 retval= RETVAL_ERROR; goto DIE_NOW;
945 }
946 pak_fn= argv[optind];
947 /***** get file list *****/
948 for( index=optind+1; index<argc; index++ ) {
949 tmp_fn= argv[index];
950 if( indirect_files ) {
951 /* add names from file tmp_fn to list */
952 if( (tmp_fd=my_open(tmp_fn,O_RDONLY,PERMISSIONS,pn)) == -1 ) {
953 retval= 1; goto DIE_NOW;
954 }
955 /* blanks are allowed ==> separator is 0x0a */
956 eof_reached= 0;
957 while( !eof_reached ) {
958 /* read file name to buffer */
959 for( i=0; i<MAX_FN_LEN; i++ ) {
960 if( (nbr=my_read(tmp_fd,&fn_buf[i],1,tmp_fn,pn)) == -1 ) {
961 retval= RETVAL_ERROR; goto DIE_NOW;
962 }
963 if( nbr == 0 ) {
964 eof_reached= 1;
965 fn_buf[i]='\0';
966 break;
967 }
968 if( fn_buf[i] == 0x0a ) {
969 fn_buf[i]='\0';
970 break;
971 }
972 }
973 if( i == MAX_FN_LEN ) {
974 fn_buf[i-1]= '\0';
975 fprintf( ERROR_CHANNEL,
976 "%s: File name `%s' from file `%s' too long.\n",
977 pn, fn_buf, tmp_fn );
978 retval= RETVAL_ERROR; goto DIE_NOW;
979 }
980 /* add file name to list */
981 file_list= add_to_list( fn_buf );
982 if( file_list == NULL ) {
983 fprintf( ERROR_CHANNEL, "%s: Allocation problem.\n", pn );
984 retval= RETVAL_ERROR; goto DIE_NOW;
985 }
986 }
987 }
988 else {
989 /* add tmp_fn to list */
990 if( strlen(tmp_fn) >= MAX_FN_LEN ) {
991 fprintf( ERROR_CHANNEL, "%s: File name `%s' too long.\n", pn, tmp_fn );
992 retval= RETVAL_ERROR; goto DIE_NOW;
993 }
994 file_list= add_to_list( tmp_fn );
995 if( file_list == NULL ) {
996 fprintf( ERROR_CHANNEL, "%s: Allocation problem.\n", pn );
997 retval= RETVAL_ERROR; goto DIE_NOW;
998 }
999 }
1000 }
1001 #ifdef DEBUG
1002 fprintf( DEBUG_CHANNEL, "DEBUG: main(): List has %d entries, namely:\n", is_in_list(file_list,"")-1 );
1003 for( i=0; i<is_in_list(file_list,"")-1; i++ )
1004 fprintf( DEBUG_CHANNEL, "DEBUG: main(): %d - %#08x - `%s'\n", i, list_entry(file_list,i), list_entry(file_list,i) );
1005 #endif
1006 /***** do the dirty work *****/
1007 switch( action ) {
1008 case ACTION_LIST: /* list table of contents */
1009 if( read_pak_header(&pak_hdr,pak_fn,pn) == -1 )
1010 { retval= RETVAL_ERROR; goto DIE_NOW; }
1011 if( (pak_nf=check_pak_header(&pak_hdr,pak_fn,pn)) == -1 )
1012 { retval= RETVAL_ERROR; goto DIE_NOW; }
1013 #ifdef DEBUG
1014 fprintf( DEBUG_CHANNEL, "DEBUG: pak size = %#08x\n",pak_hdr.pak_sze);
1015 fprintf( DEBUG_CHANNEL, "DEBUG: toc offset= %#08x\n",pak_hdr.toc_off);
1016 fprintf( DEBUG_CHANNEL, "DEBUG: toc size = %#08x\n",pak_hdr.toc_sze);
1017 fprintf( DEBUG_CHANNEL, "DEBUG: files = %d\n",pak_nf);
1018 #endif
1019 if( (pak_toc=read_pak_toc(&pak_hdr,pak_fn,pn)) == NULL && pak_nf )
1020 { retval= RETVAL_ERROR; goto DIE_NOW; }
1021 if( check_pak_toc(pak_toc,pak_nf,pak_fn,pn) == -1 )
1022 { retval= RETVAL_ERROR; goto DIE_NOW; }
1023 if( list_pak_toc(pak_toc,pak_nf,file_list,verbose,force,pak_fn,pn) == -1 )
1024 { retval= RETVAL_ERROR; goto DIE_NOW; }
1025 break;
1026 case ACTION_EXTRACT: /* extract file(s) */
1027 if( read_pak_header(&pak_hdr,pak_fn,pn) == -1 )
1028 { retval= RETVAL_ERROR; goto DIE_NOW; }
1029 if( (pak_nf=check_pak_header(&pak_hdr,pak_fn,pn)) == -1 )
1030 { retval= RETVAL_ERROR; goto DIE_NOW; }
1031 if( (pak_toc=read_pak_toc(&pak_hdr,pak_fn,pn)) == NULL && pak_nf )
1032 { retval= RETVAL_ERROR; goto DIE_NOW; }
1033 if( check_pak_toc(pak_toc,pak_nf,pak_fn,pn) == -1 )
1034 { retval= RETVAL_ERROR; goto DIE_NOW; }
1035 if( extract_pak(pak_toc,pak_nf,file_list,verbose,force,pak_fn,pn) == -1 )
1036 { retval= RETVAL_ERROR; goto DIE_NOW; }
1037 break;
1038 case ACTION_CREATE: /* create new pak file */
1039 if( init_pak_header(&pak_hdr) == -1 )
1040 { retval= RETVAL_ERROR; goto DIE_NOW; }
1041 if( write_pak_header(&pak_hdr,O_TRUNC,pak_fn,pn) == -1 )
1042 { retval= RETVAL_ERROR; goto DIE_NOW; }
1043 if( (pak_toc=write_pak(&pak_hdr,file_list,verbose,force,pak_fn,pn)) == NULL && pak_hdr.toc_sze )
1044 { retval= RETVAL_ERROR; goto DIE_NOW; }
1045 if( write_pak_toc(&pak_hdr,pak_toc,pak_fn,pn) == -1 )
1046 { retval= RETVAL_ERROR; goto DIE_NOW; }
1047 if( write_pak_header(&pak_hdr,0x00,pak_fn,pn) == -1 )
1048 { retval= RETVAL_ERROR; goto DIE_NOW; }
1049 break;
1050 default: /* program error */
1051 fprintf( ERROR_CHANNEL, "%s: Action %d unknown! E-mail me at %s.\n",
1052 pn, action, MY_EMAIL_ADDRESS );
1053 retval= RETVAL_BUG; goto DIE_NOW;
1054 }
1055 DIE_NOW:
1056 free( pak_toc );
1057 free( file_list );
1058 close( pak_fd );
1059 close( tmp_fd );
1060 if( action==ACTION_CREATE )
1061 { /* remove pak file on error */
1062 #ifndef ALLOW_EMPTY_ARCHIVES
1063 if( pak_hdr.toc_sze==0 ) {
1064 fprintf( ERROR_CHANNEL, "%s: Creation of empty archives not allowed.\n",
1065 pn );
1066 retval= RETVAL_ERROR;
1067 }
1068 #endif
1069 if( retval==RETVAL_ERROR && pak_fn!=NULL ) {
1070 /* let's do it quick and dirty... */
1071 char cmd[PAK_TOC_ENTRY_SIZE];
1072 sprintf( cmd, "rm -f %s", pak_fn );
1073 system( cmd );
1074 }
1075 }
1076 exit( retval );
1077 }
1078