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