home *** CD-ROM | disk | FTP | other *** search
- /* ExTARct.c
- * Tar file extractor.
- * (C) 1995 Kevin F. Quinn
- * Email: kevq@banana.demon.co.uk
- *
- * Rationale - to provide an automated facility for extracting tar archives
- * in a RiscOS-friendly way, i.e. reversing name/type, and inserting an extra
- * directory level to cope with the (grr!) 77-entry directory limit.
- *
- * Syntax:
- * ExTARct [options] <tarfile>
- * where options are as follows:
- * -v The ubiquitous verbose option
- * -d <number> The number of files to one directory (default 60)
- * -t <filename> Filename listing types to swap, tagged for directory split
- * (default is no swaps, no directory splits)
- * -l List operations (i.e. file mappings) without doing anything really
- * -p <prefix> Prefix for directory splits (default none)
- * -c Enable filename crunching (uses fileswitch truncation by default)
- * Unimplemented as yet.
- *
- */
-
- /*
- * Version 0.1
- * Crunching not supported yet.
- * Gets very slow when it is searching split subdirectories.
- */
-
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "tar.h"
- #include "kernel.h"
- #include "swis.h"
-
- #define FALSE (0==1)
- #define TRUE (1==1)
-
- #define MAXTYPES 256
- #define MAXLEN 256
- #define MAXENTS 77
- #define BUFSIZE 78*256
-
- static int dsize;
- static int verbose;
- static int list;
- static int crunch;
- static char *types[MAXTYPES];
- static int splits[MAXTYPES];
- static int tc;
- static char *prefix;
-
- static char buf[BUFSIZE];
-
-
- int makedir(char *filename) {
- char directory[MAXLEN];
- char *cptr;
- _kernel_oserror *kerr=NULL;
- _kernel_swi_regs rin, rout;
-
- if (*filename=='\0') return(FALSE);
- cptr=filename+strlen(filename);
- while (cptr>filename && *cptr!='.') cptr--;
- if (*cptr=='.') {
- strcpy(directory,filename);
- directory[cptr-filename]='\0';
- rin.r[0]=8;
- rin.r[1]=(int)directory;
- rin.r[2]=0; /* default directory size */
- kerr=_kernel_swi(OS_File,&rin,&rout);
- if (kerr==NULL) {
- return(TRUE);
- } else {
- if (makedir(directory)) {
- return(makedir(filename));
- } else {
- return(FALSE);
- }
- }
- } else {
- return(TRUE); /* No directory; no need to create */
- }
- }
-
- /* Convert octal string to number */
- static int otoi(char *str) {
- int num=0;
- while (*str==' ') str++;
- while (*str>='0' && *str<'8') {
- num=num*8+((*str)-'0');
- str++;
- }
- return(num);
- }
-
-
- static void roify(char *str) {
- /* Convert the following:
- *
- * / -> .
- * . -> /
- * :,*,#,$,&,@,^,%,\,|,",SPACE -> ~<n> (where n=0..9,A,B)
- * ~ -> ~~
- *
- * Note that this conversion is reversible.
- * Note also that no check is performed that MAXLEN is not exceeded,
- * which in theory is possible. However, tar names are 100 chars max,
- * so the problem should not arise.
- */
- char tstr[MAXLEN];
- int i=0;
- strcpy(tstr,str);
- while (tstr[i]!='\0') {
- switch (tstr[i]) {
- /* Swap directory and filetype separators*/
- case '/': *str='.'; break;
- case '.': *str='/'; break;
-
- /* Escape the nasties */
- case ':': *str++='~'; *str='0'; break;
- case '*': *str++='~'; *str='1'; break;
- case '#': *str++='~'; *str='2'; break;
- case '$': *str++='~'; *str='3'; break;
- case '&': *str++='~'; *str='4'; break;
- case '@': *str++='~'; *str='5'; break;
- case '^': *str++='~'; *str='6'; break;
- case '%': *str++='~'; *str='7'; break;
- case '\\': *str++='~'; *str='8'; break;
- case '|': *str++='~'; *str='9'; break;
- case '"': *str++='~'; *str='A'; break;
- case ' ': *str++='~'; *str='B'; break;
- case '~': *str++='~'; *str='~'; break;
-
- default:
- /* character is OK */;
- }
- i++;
- str++;
- }
- }
-
-
- static int translatename(const char *tarname, char *roname) {
- /* Right; this is the cunning bit.
- *
- * Name translation.
- * split to Directory,filename, converting /->., .->/ in directory name
- * split filename to name.type (search back from end to first '.')
- * if name is empty, return FALSE
- * if no type
- * roname=directory.name
- * else
- * if type is in types list
- * if splits[n] (& in types list)
- * find first subdirectory in directory 1,2,3... which has less than dsize entries
- * roname=directory.sub.type.name
- * else
- * roname=directory.type.name
- * else
- * roname=directory.name/type
- * return TRUE;
- */
-
- char tname[MAXLEN];
- char *direct;
- char *name;
- char *type;
-
- char *tptr;
- int tn, fc;
- int found;
- int subnum;
- char scratch[MAXLEN];
- _kernel_oserror *kerr=NULL;
- _kernel_swi_regs rin, rout;
- int tmpind;
-
- strcpy(tname,tarname);
- tptr=tname+strlen(tname);
- while (tptr>tname && *tptr!='/') tptr--;
- if (*tptr!='/') {
- direct=NULL;
- name=tname;
- } else {
- direct=tname;
- *tptr='\0';
- name=tptr+1;
- }
- if (*name!='\0') { /* no name => just a directory */
- tptr=name+strlen(name);
- while (tptr>name && *tptr!='.') tptr--;
- if (*tptr!='.') {
- type=NULL;
- } else {
- *tptr='\0';
- type=tptr+1;
- }
- } else {
- name=NULL;
- type=NULL;
- }/* if not just a directory */
- /* Sort out any RO special character problems */
- if (direct!=NULL) roify(direct);
- if (name!=NULL) roify(name);
- if (type!=NULL) roify(type);
-
- if (name!=NULL) {/* if not just a directory */
- if (type==NULL) {
- /* roname=directory.name*/
- if (direct!=NULL) {
- strcpy(roname,direct);
- tptr=roname+strlen(direct);
- *tptr++='.';
- *tptr='\0';
- } else {
- tptr=roname;
- }
- strcpy(tptr,name);
- } else {
- /* if type is in types list*/
- tn=0;
- /* The 'while' condition below avoids evaluating types[tn] when no types are valid (i.e. tn=tc=0) */
- while ((tn<tc)?(strcmp(types[tn],type)!=0):FALSE) tn++;
- if (tn<tc) {
- /*
- * find first subdirectory in directory 1,2,3... which has less than dsize entries
- * roname=directory.sub.type.name
- */
- if (direct!=NULL) {
- strcpy(scratch,direct);
- tptr=scratch+strlen(direct);
- *tptr++='.';
- *tptr='\0';
- } else {
- tptr=scratch;
- *tptr='\0';
- }
- if (splits[tn]) {
- if (prefix!=NULL) {
- strcpy(tptr,prefix);
- tptr=tptr+strlen(prefix);
- *tptr='\0';
- }
- subnum=0;
- found=FALSE;
- while (!found) {
- tptr[0]=(subnum / 10)+'0';
- tptr[1]=(subnum % 10)+'0';
- tptr[2]='.';
- tmpind=0;
- while (type[tmpind]>'\0') { /* if splits[tn], type!=NULL... */
- tptr[3+tmpind]=type[tmpind];
- tmpind++;
- }
- tptr[3+tmpind]='\0';
- fc=0;
- rin.r[0]=9;
- rin.r[1]=(int)scratch;
- rin.r[2]=(int)buf;
- rin.r[3]=MAXENTS;
- rin.r[4]=0;
- rin.r[5]=BUFSIZE;
- rin.r[6]=(int)"*";
- while ((rin.r[4]!=-1) && (kerr==NULL)) {
- kerr=_kernel_swi(OS_GBPB,&rin,&rout);
- if (kerr==NULL) {
- fc+=rout.r[3];
- rin.r[4]=rout.r[4];
- }
- }
- if (fc<dsize) {
- found=TRUE;
- } else {
- subnum++;
- }
- }
- tptr[2]='.';
- tptr[3]='\0';
- } /* if split */
- strcpy(roname,scratch);
- tptr=roname+strlen(scratch);
- if (type!=NULL) {
- strcpy(tptr,type);
- tptr+=strlen(type);
- *tptr++='.';
- }
- strcpy(tptr,name);
- } else {
- /* else roname=directory.name/type */
- if (direct!=NULL) {
- strcpy(roname,direct);
- tptr=roname+strlen(direct);
- *tptr++='.';
- } else {
- tptr=roname;
- }
- strcpy(tptr,name);
- if (type!=NULL) {
- tptr=tptr+strlen(name);
- *tptr++='/';
- strcpy(tptr,type);
- }
- } /* if type in types list */
- } /* type is not null */
- } else {
- strcpy(roname,direct);
- } /* if just a directory */
- if (name!=NULL) {
- return(TRUE);
- } else {
- return(FALSE);
- }
- }
-
-
- int main(int argc, char **argv) {
- char *tarfile=NULL;
- char *tfile=NULL;
- int tabort=FALSE;
- FILE *fhandle;
- FILE *ohandle;
- FILE *lhandle;
- char line[MAXLEN];
- char *lc=NULL;
- int ln, titem;
- HBlock block;
- _kernel_oserror *kerr;
- _kernel_swi_regs rin, rout;
- int filesize;
- char filename[MAXLEN];
- char linkname[MAXLEN];
- int endblock;
- char *cptr;
-
- /* Initialise global data */
- verbose=FALSE;
- list=FALSE;
- dsize=-1;
- crunch=FALSE;
- tc=0;
- prefix=NULL;
-
- /* Process arguments */
- argv++; /* skip filename */
- while (argc>0) {
- switch (argv[0][0]) {
- case '-':
- switch ((argv[0][1] | 0x20)) {
- case 'v':
- verbose=TRUE;
- break;
- case 'l':
- list=TRUE;
- break;
- case 'c':
- crunch=TRUE;
- break;
- case 'd':
- if (dsize!=-1) {
- (void) fprintf(stderr, "ExTARct: Only one -d value, please\n");
- tabort=TRUE;
- } else {
- if (argc>1) {
- dsize=atoi(argv[1]);
- argv++;
- argc--;
- } else {
- (void) fprintf(stderr, "ExTARct: -d requires parameter (number)\n");
- tabort=TRUE;
- }
- }
- break;
- case 't':
- if (tfile!=NULL) {
- (void) fprintf(stderr, "ExTARct: Only one tfile, please\n");
- tabort=TRUE;
- } else {
- if (argc>1) {
- tfile=argv[1];
- argv++;
- argc--;
- } else {
- (void) fprintf(stderr, "ExTARct: -t requires parameter (filename)\n");
- tabort=TRUE;
- }
- }
- break;
- case 'p':
- if (prefix!=NULL) {
- (void) fprintf(stderr, "ExTARct: Only one -p value, please\n");
- tabort=TRUE;
- } else {
- if (argc>1) {
- prefix=argv[1];
- argv++;
- argc--;
- } else {
- (void) fprintf(stderr, "ExTARct: -p requires parameter (prefix string)\n");
- tabort=TRUE;
- }
- }
- break;
- default:
- (void) fprintf(stderr, "ExTARct: Bad option %c\n", argv[0][1]);
- tabort=TRUE;
- }
- break;
- default:
- /* Must be filename */
- if (argv[0]!=NULL) tarfile=argv[0];
- }
- argv++;
- argc--;
- }
- if (dsize==-1) dsize=60;
-
- /* Read configuration file */
- if (tfile != NULL) {
- if ((fhandle=fopen(tfile,"r"))==NULL) {
- (void) fprintf(stderr, "ExTARct: Open failed for T file\n");
- tabort=TRUE;
- }
- while (!feof(fhandle) && !tabort) {
- if (fgets(line, MAXLEN, fhandle)==NULL) {
- if (!feof(fhandle)) {
- (void) fprintf(stderr, "ExTARct: Read failed for T file\n");
- tabort=TRUE;
- }
- } else {
- lc=line;
- while (*lc==' ' || *lc=='\t') lc++;
- if (*lc!='#') {
- splits[tc]=FALSE;
- ln=0;
- while (lc[ln]>' ') ln++;
- types[tc]=malloc(ln+1);
- strncpy(types[tc],lc,ln);
- types[tc][ln]='\0';
- while (lc[ln]==' ' || lc[ln]=='\t') ln++;
- if (lc[ln]>' ') {
- switch (lc[ln]) {
- case '*':
- splits[tc]=TRUE;
- break;
- default:
- (void) fprintf(stderr, "ExTARct: Bad switch in Tfile (%c)\n", lc[ln]);
- tabort=TRUE;
- }
- }
- tc++;
- }
- }
- }
- fclose(fhandle);
- }
-
- /* tarfile MUST be present :-) Don't really want (can't be bothered...) to use stdin by default */
- if (tarfile==NULL) {
- (void) fprintf(stderr, "ExTARct: Tar file is mandatory\n");
- tabort=TRUE;
- }
-
- /* Display accumulated settings */
- if (verbose) {
- (void) fprintf(stdout, "ExTARct v0.01 (C) 1995 Kevin F. Quinn\n Email kevq@banana.demon.co.uk\n\n");
- (void) fprintf(stdout, "Settings:\n");
- (void) fprintf(stdout, " Directory limit %d\n", dsize);
- (void) fprintf(stdout, " Verbose ON\n");
- (void) fprintf(stdout, " Prefix: %s\n",(prefix==NULL)?"(none)":prefix);
- (void) fprintf(stdout, " Types:%s\n",(tc==0)?" (none)":"");
- titem=0;
- while (titem<tc) {
- (void) fprintf(stdout, " %s%s\n",types[titem],splits[titem]?" (split)":"");
- titem++;
- }
- }
-
- /* Report error and exit, if anything untoward was detected when reading options */
- if (tabort) {
- (void) fprintf(stderr, "Syntax:\n ExTARct [options] <tarfile>");
- (void) fprintf(stderr, " where options are as follows:\n");
- (void) fprintf(stderr, " -v The ubiquitous verbose option\n");
- (void) fprintf(stderr, " -d <number> The number of files to one directory (default 60)\n");
- (void) fprintf(stderr, " -t <filename> Filename listing types to swap, tagged for directory split\n");
- (void) fprintf(stderr, " (default is no swaps, no directory splits)\n");
- (void) fprintf(stderr, " -p <prefix> Prefix for split subdirectories (default none)\n");
- (void) fprintf(stderr, " -l List operations (i.e. filename mappings) only\n");
- return(EXIT_FAILURE);
- }
-
-
- /* Now on to the real business of extracting the archive */
- if ((fhandle=fopen(tarfile,"rb"))==NULL) {
- (void) fprintf(stderr, "Unable to open file %s\n", tarfile);
- return(EXIT_FAILURE);
- } /* if fhandle == NULL */
- endblock=FALSE;
- while (!feof(fhandle) && !tabort && !endblock) {
- if (fread(&block, sizeof(HBlock), 1, fhandle)!=1) {
- (void) fprintf(stderr, "Bad block; must be 512 bytes\n");
- tabort=TRUE;
- } else {
- if (block.dbuf.name[0]=='\0') endblock=TRUE;
- if (!endblock) {
- if (!translatename(block.dbuf.name, filename)) { /* directory only*/
- if (list) {
- (void) fprintf(stdout, "Directory %s\n",filename);
- } else {
- if (verbose) (void) fprintf(stdout, "Directory %s\n",filename);
- strcpy(line,filename); /* Need to tag '.' on end of directory */
- cptr=line+strlen(filename);
- *cptr++='.';
- *cptr='\0';
- if (!makedir(line)) {
- (void) fprintf(stdout, "Unable to create directory %s (%s)\n",
- filename,block.dbuf.name);
- }
- }
- } else {
- if (block.dbuf.linkflag=='1' || block.dbuf.linkflag=='2') {
- /* Entry is a link.
- * Create a LinkFS image. These are files of type FC0, which simply
- * contain the canonical name of the file, followed by a '.' and a '\0'.
- * Only symbolic links are available, so I generate a symbolic link for hard links.
- */
- translatename(block.dbuf.linkname, linkname);
- if (list) {
- (void) fprintf(stdout, "Linking %s to %s (original names %s and %s)\n",
- filename,linkname,block.dbuf.name,block.dbuf.linkname);
- } else {
- if (verbose)
- (void) fprintf(stdout, "Linking %s to %s (original names %s and %s)\n",
- filename,linkname,block.dbuf.name,block.dbuf.linkname);
- if ((lhandle=fopen(linkname,"wb"))==NULL) {
- /* Try to create directory (if relevant) - note this is necessary for the "sub"ed files */
- if (!makedir(linkname)) {
- (void) fprintf(stderr, "Unable to create directory for link %s (to %s)\n",linkname,filename);
- tabort=TRUE;
- } else {
- if ((lhandle=fopen(linkname,"wb"))==NULL) {
- (void) fprintf(stderr, "Unable to create link %s (to %s)\n",linkname,filename);
- tabort=TRUE;
- }
- }
- } /* ohandle==NULL*/
- if (!tabort) {
- /* Write link */
- fputs(filename,lhandle);
- fputc('.',lhandle);
- fputc('\0',lhandle);
- fclose(lhandle);
- /* Settype to 0xFC0 */
- rin.r[0]=18;
- rin.r[1]=(int)linkname;
- rin.r[2]=0xFC0;
- kerr=_kernel_swi(OS_File, &rin, &rout);
- if (kerr!=NULL)
- (void) fprintf(stderr, "Unable to settype %s to 0xFC0\n",linkname);
- } /* if lhandle==NULL */
- } /* if list */
- } else {
- filesize=otoi(block.dbuf.size);
- if (list) {
- (void) fprintf(stdout, "Creating %s (original name %s)\n",
- filename,block.dbuf.name);
- while (filesize>0 && !tabort) {
- if (fread(&block, sizeof(HBlock), 1, fhandle)!=1) {
- (void) fprintf(stderr, "Read failed\n");
- tabort=TRUE;
- } /* if fread(block) */
- filesize=(filesize>TBLOCK)?(filesize-TBLOCK):0;
- } /* while filesize>0 */
- } else {
- if (verbose)
- (void) fprintf(stdout, "Creating %s (original name %s)\n",
- filename,block.dbuf.name);
- if ((ohandle=fopen(filename,"wb"))==NULL) {
- /* Try to create directory (if relevant) - note this is necessary for the "sub"ed files */
- if (!makedir(filename)) {
- (void) fprintf(stderr, "Couldn't create directory for %s\n", filename);
- tabort=TRUE;
- } else {
- if ((ohandle=fopen(filename,"wb"))==NULL) {
- (void) fprintf(stderr, "Couldn't create %s\n", filename);
- tabort=TRUE;
- }
- }
- } /* ohandle==NULL*/
- if (!tabort) {
- while (filesize>0 && !tabort) {
- if (fread(&block, sizeof(HBlock), 1, fhandle)!=1) {
- (void) fprintf(stderr, "Read failed\n");
- tabort=TRUE;
- } else {
- if (fwrite(&block, (filesize>TBLOCK)?TBLOCK:filesize, 1, ohandle)!=1) {
- (void) fprintf(stderr, "Write failed\n");
- tabort=TRUE;
- } /* if fwrite(block) */
- filesize=(filesize>TBLOCK)?(filesize-TBLOCK):0;
- } /* if fread(block) */
- } /* while filesize>0 */
- fclose(ohandle);
- } /* !tabort */
- } /* if list */
- } /* if not a link */
- } /* if directory */
- } /* if block.dbuf.name[0]!='\0' */
- } /* if fread(block) */
- } /* while (!end of file) */
- if (tabort) {
- (void) fprintf(stderr, "Processing aborted\n");
- return(EXIT_FAILURE);
- } /* tabort */
- return(EXIT_SUCCESS);
- }
-