nomarch-1.4/0040711000175000001440000000000010445251045011531 5ustar rususersnomarch-1.4/main.c0100600000175000001440000004144610445247533012636 0ustar rususers/* nomarch 1.4 - extract old `.arc' archives. * Copyright (C) 2001-2006 Russell Marks. * * main.c - most of the non-extraction stuff. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define NOMARCH_VER "1.4" #include #include #include #include #include #include #include #include #include #include "readrle.h" #include "readhuff.h" #include "readlzw.h" char *archive_filename=NULL; char **archive_matchstrs=NULL; /* NULL, or points to argv+N */ int num_matchstrs=0; int opt_list=0,opt_print=0,opt_test=0,opt_verbose=0; int opt_preservecase=0; int quiet=0; struct archived_file_header_tag { unsigned char method; char name[13]; unsigned long compressed_size; /* 4 bytes in file */ unsigned int date,time,crc; /* 2 bytes each in file */ unsigned long orig_size; /* 4 bytes in file */ int has_crc; }; int maybe_downcase(int c) { if(opt_preservecase) return(c); return(tolower(c)); } /* there is no overall header for the archive, but there's a header * for each file stored in it. * returns zero if we couldn't get a header. * NB: a header with method zero marks EOF. */ int read_file_header(FILE *in,struct archived_file_header_tag *hdrp) { unsigned char buf[4+2+2+2+4]; /* used to read size1/date/time/crc/size2 */ int bufsiz=sizeof(buf); int method_high; int c,f; hdrp->method=0xff; if(fgetc(in)!=0x1a) return(0); if((c=fgetc(in))==EOF) return(0); /* allow for the spark archive variant's alternate method encoding */ method_high=(c>>7); hdrp->method=(c&127); /* zero if EOF, which also means no further `header' */ if(hdrp->method==0) return(1); /* `old' version of uncompressed storage was weird */ if(hdrp->method==1) bufsiz-=4; /* no `orig_size' field */ if(fread(hdrp->name,1,sizeof(hdrp->name),in)!=sizeof(hdrp->name) || fread(buf,1,bufsiz,in)!=bufsiz) return(0); /* extract the bits from buf */ hdrp->compressed_size=(buf[0]|(buf[1]<<8)|(buf[2]<<16)|(buf[3]<<24)); hdrp->date=(buf[4]|(buf[5]<<8)); hdrp->time=(buf[6]|(buf[7]<<8)); hdrp->crc=(buf[8]|(buf[9]<<8)); /* yes, only 16-bit CRC */ hdrp->has_crc=1; if(hdrp->method==1) hdrp->orig_size=hdrp->compressed_size; else hdrp->orig_size=(buf[10]|(buf[11]<<8)|(buf[12]<<16)|(buf[13]<<24)); /* make *sure* name is asciiz */ hdrp->name[12]=0; /* strip top bits, and lowercase the name */ for(f=0;fname);f++) hdrp->name[f]=maybe_downcase(hdrp->name[f]&127); /* lose the possible extra bytes in spark archives */ if(method_high) { if(fread(buf,1,12,in)!=12) return(0); /* has a weird recursive-.arc file scheme for subdirs, * and since these are supposed to be dealt with inline * (though they aren't here) the CRCs could be junk. * So check for it being marked as a stored dir. */ if(hdrp->method==2 && buf[3]==0xff && buf[2]==0xfd && buf[1]==0xdc) hdrp->has_crc=0; } return(1); } /* self-extracting archives, for both CP/M and MS-DOS, have up to * 3 bytes before the initial ^Z. This skips those if present. * Returns zero if there's an input error, or we fail to find ^Z in * the first 4 bytes. * * This should work with self-extracting archives for CP/M * (e.g. unarc16.ark), and those produced by `arc'. It won't work with * pkpak self-extracting archives, for two reasons: * * - they have 4 bytes before the ^Z. * - they have an EOF member (zero byte) right after that, giving you * an archive containing no files (grrr). * * So I thought it was better (and less confusing) to effectively stick * with the not-an-archive error for those. :-) */ int skip_sfx_header(FILE *in) { int c,f,got=0; for(f=0;f<4;f++) { if((c=fgetc(in))==EOF) return(0); if(c==0x1a) { got=1; ungetc(c,in); break; } } return(got); } /* read file data, assuming header has just been read from in * and hdrp's data matches it. Caller is responsible for freeing * the memory allocated. * Returns NULL for file I/O error only; OOM is fatal (doesn't return). */ unsigned char *read_file_data(FILE *in,struct archived_file_header_tag *hdrp) { unsigned char *data; int siz=hdrp->compressed_size; if((data=malloc(siz))==NULL) fprintf(stderr,"nomarch: out of memory!\n"),exit(1); if(fread(data,1,siz,in)!=siz) { free(data); data=NULL; } return(data); } /* variant which just skips past the data */ int skip_file_data(FILE *in,struct archived_file_header_tag *hdrp) { int siz=hdrp->compressed_size; int f; for(f=0;f>1)^0xA001); else crc>>=1; } } crc&=0xffff; return(crc); } /* convert MS-DOS time format to string */ char *mkdatetimestr(unsigned int hdate,unsigned int htime) { static char buf[128]; int day,month,year,hour,min; year=1980+(hdate>>9); month=((hdate>>5)&15); day=(hdate&31); hour=(htime>>11); min=((htime>>5)&63); /* seconds ignored */ sprintf(buf,"%4d-%02d-%02d %02d:%02d",year,month,day,hour,min); return(buf); } /* convert to time_t */ time_t mkdatetimet(unsigned int hdate,unsigned int htime) { struct tm tms; tms.tm_year=80+(hdate>>9); tms.tm_mon=((hdate>>5)&15)-1; tms.tm_mday=(hdate&31); tms.tm_hour=(htime>>11); tms.tm_min=((htime>>5)&63); tms.tm_sec=(htime&31)*2; tms.tm_isdst=-1; /* i.e. unknown */ return(mktime(&tms)); } /* simple wildcard-matching code. It implements shell-like * `*' and `?', but not `[]' or `{}'. * XXX might be nice to replace this with something better :-) */ int is_match(char *filename,char *wildcard) { char *ptr=filename,*match=wildcard; char *tmp,*tmp2; int old; int ok=1; while(*ptr && *match && ok) { switch(*match) { case '*': /* need to check that everything up to the next * or ? matches * at some point from here onwards */ /* skip the `*', and any * or ? following */ while(*match=='*' || *match=='?') match++; /* find the next * or ?, or the end of the name */ tmp=strchr(match,'*'); if(tmp==NULL) tmp=strchr(match,'?'); if(tmp==NULL) tmp=match+strlen(match); tmp--; /* if *match is NUL, the * was the last char, so it's already matched * if tmp+1-match>strlen(ptr) then it can't possibly match */ if(*match==0) ptr+=strlen(ptr); /* just `*', so skip to end */ else { if(tmp+1-match>strlen(ptr)) ok=0; else { /* go forward through the string, attempting to match the text * from match to tmp (inclusive) each time */ old=tmp[1]; tmp[1]=0; if((tmp2=strstr(ptr,match))==NULL) ok=0; /* didn't match */ else ptr=tmp2; tmp[1]=old; } } break; case '?': match++; ptr++; /* always matches */ break; default: if(*match!=*ptr) ok=0; else { match++; ptr++; } } } if(*ptr || *match) ok=0; /* if any text left, it didn't match */ return(ok); } /* see if file matches any of the match strings specified * on the cmdline. */ int file_matches(char *filename) { int f; if(!num_matchstrs) return(1); for(f=0;f1) { archive_matchstrs=argv+optind+1; num_matchstrs=argc-optind-1; } if(argc==optind) /* if no filename */ usage_help(),exit(1); archive_filename=argv[optind]; if(opt_print) quiet=1; } int main(int argc,char *argv[]) { parse_options(argc,argv); if(opt_list && opt_test) opt_list=0; /* test wins */ if(opt_list) exit(arc_list(opt_verbose)); exit(arc_extract_or_test(opt_test)); } nomarch-1.4/readlzw.c0100600000175000001440000002144310445247503013352 0ustar rususers/* nomarch 1.4 - extract old `.arc' archives. * Copyright (C) 2001-2006 Russell Marks. See main.c for license details. * * readlzw.c - read (RLE+)LZW-compressed files. * * This is based on zgv's GIF reader. The LZW stuff is much the same, but * figuring out the details of the rather bizarre encoding involved much * wall therapy. %-( */ #include #include #include #include #include "readrle.h" #include "readlzw.h" /* now this is for the string table. * the st_ptr array stores which pos to back reference to, * each string is [...]+ end char, [...] is traced back through * the 'pointer' (index really), then back through the next, etc. * a 'null pointer' is = to UNUSED. * the st_chr array gives the end char for each. * an unoccupied slot is = to UNUSED. */ #define UNUSED (-1) #define REALMAXSTR 65536 static int st_ptr[REALMAXSTR],st_chr[REALMAXSTR],st_last; static int st_ptr1st[REALMAXSTR]; /* this is for the byte -> bits mangler: * dc_bitbox holds the bits, dc_bitsleft is number of bits left in dc_bitbox, */ static int dc_bitbox,dc_bitsleft; static unsigned char *data_in_point,*data_in_max; static unsigned char *data_out_point,*data_out_max; static int codeofs; static int global_use_rle,oldver; static int maxstr; static int st_oldverhashlinks[4096]; /* only used for 12-bit types */ /* prototypes */ void code_resync(int old); void inittable(int orgcsize); int addstring(int oldcode,int chr); int readcode(int *newcode,int numbits); void outputstring(int code); void outputchr(int chr); int findfirstchr(int code); unsigned char *convert_lzw_dynamic(unsigned char *data_in, int max_bits,int use_rle, unsigned long in_len, unsigned long orig_len) { unsigned char *data_out; int csize,orgcsize; int newcode,oldcode,k=0; int first=1,noadd; global_use_rle=use_rle; maxstr=(1<st_last+1) fprintf(stderr,"warning: bad LZW code\n"); #endif /* k=findfirstchr(oldcode);*/ /* don't think I actually need this */ outputstring(oldcode); outputchr(k); } if(st_last!=maxstr-1) { if(!noadd) { if(!addstring(oldcode,k)) { /* XXX I think this is meant to be non-fatal? * well, nothing for now, anyway... */ } if(st_last!=maxstr-1 && st_last==((1<>6)&0xfff); /* first, check link chain from there */ while(st_chr[hashval]!=UNUSED && st_oldverhashlinks[hashval]!=UNUSED) hashval=st_oldverhashlinks[hashval]; /* make sure we return early if possible to avoid adding link */ if(st_chr[hashval]==UNUSED) return(hashval); lasthash=hashval; /* slightly odd approach if it's not in that - first try skipping * 101 entries, then try them one-by-one. It should be impossible * for this to loop indefinitely, if the table isn't full. (And we * shouldn't have been called if it was full...) */ hashval+=101; hashval&=0xfff; if(st_chr[hashval]!=UNUSED) { for(f=0;f=maxstr) return(1); st_ptr[idx]=oldcode; if(st_ptr[oldcode]==UNUSED) /* if we're pointing to a root... */ st_ptr1st[idx]=oldcode; /* then that holds the first char */ else /* otherwise... */ st_ptr1st[idx]=st_ptr1st[oldcode]; /* use their pointer to first */ return(1); } /* read a code of bitlength numbits */ int readcode(int *newcode,int numbits) { int bitsfilled,got; bitsfilled=got=0; (*newcode)=0; while(bitsfilled=data_in_max) return(0); dc_bitbox=*data_in_point++; dc_bitsleft=8; } if(dc_bitsleft>8)<<(numbits-bitsfilled)); dc_bitsleft-=got; } else { (*newcode)|=((dc_bitbox&((1<>=got; dc_bitsleft-=got; bitsfilled+=got; } } if((*newcode)<0 || (*newcode)>maxstr-1) return(0); /* yuck... see code_resync() for explanation */ codeofs++; codeofs&=7; return(1); } void outputstring(int code) { static int buf[REALMAXSTR]; int *ptr=buf; while(st_ptr[code]!=UNUSED && ptrbuf) outputchr(*--ptr); } static void rawoutput(int byte) { if(data_out_point Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. nomarch-1.4/readlzw.h0100600000175000001440000000062207524347433013362 0ustar rususers/* nomarch 1.3 - extract old `.arc' archives. * Copyright (C) 2001,2002 Russell Marks. See main.c for license details. * * readlzw.h */ extern unsigned char *convert_lzw_dynamic(unsigned char *data_in, int bits,int use_rle, unsigned long in_len, unsigned long orig_len); nomarch-1.4/README0100600000175000001440000000470010445250412012404 0ustar rususersnomarch 1.4 - extract old `.arc' archives. Copyright (C) 2001-2006 Russell Marks. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Description ----------- nomarch lists/extracts/tests `.arc' archives. (It also handles `.ark' files, they're exactly the same.) This is a *very* outdated file format which should never be used for anything new, but unfortunately, you can still run into it every so often (especially if you mess about with old CP/M stuff). So nomarch is handy as a way to deal with these files. For more on how nomarch works and how to use it, do `man nomarch' once it's installed. Installation ------------ Check the Makefile is ok (it should be), then do `make' and (as root) `make install'. Why not just use `arc'? ----------------------- `arc' is (or certainly *used* to be) non-Free. For a while this didn't annoy me that much, and I put up with using it because there wasn't really anything else I could use. But once I realised that I could probably write my own extraction program, and that some old .arc files aren't likely to go away (for example, the Walnut Creek CP/M CD-ROM contains well over a thousand), I thought I'd try writing something a bit more useful than a picture viewer for once. :-) Why the name? ------------- Since there are at least two different programs called `unarc' already (one is an old CP/M program, the other isn't .arc-related), I had to pick something a bit more obscure. I considered `arcbgon', but I thought that was a little tacky. :-) When I noticed that there was a word looking so similar to `no more arc', I went with that instead, even if the mundane meaning is a little, uh, grandiose... (Though it looks like it might also be Spanish for `filename' or something, which could be a bit weird. Oh well.) Contacting me ------------- You can email me at rus@svgalib.org. Share and enjoy! -Rus. nomarch-1.4/NEWS0100600000175000001440000000352310445250662012234 0ustar rususers* Changes between versions -*- mode:indented-text; mode:outline-minor -*- This file (NEWS) documents the significant changes between all versions of nomarch, with the newest changes first. * Changes in nomarch 1.4 Fixed a bug which could hang nomarch with corrupt files. * Changes in nomarch 1.3 Added compression methods 5 and 6, which seem to be along the same lines as v1.0 of the CP/M `crunch' scheme (so I was able to adapt some code from lbrate). This isn't very well tested though - all I know is that it works on the single method-6 test file I have - so if anyone has any more files using these methods they can test it on (use `nomarch -lv' to check the methods used by a file) I'd appreciate feedback on whether it works or not. :-) Self-extracting archives (for both MS-DOS and CP/M) can be read. (It doesn't support those produced with pkpak, but even pkunpak can't read those.) Now supports the Acorn Archimedes's variant "Spark" format (including the extra compression method), which is based on the more usual .arc. Any `subdirectories' are extracted as the .arc files they really are. main.c now uses unistd.h rather than getopt.h, which was troublesome on some systems. Thanks to Geoff Gibbs for spotting this. * Changes in nomarch 1.2 Added `-p' option, to extract files to standard output. Thanks to Hilko Bengen for reminding me about this one. Fixed DST-related bug which caused some file times to be wrong by an hour. * Changes in nomarch 1.1 New `-U' option, to use uppercase archive member filenames - or more precisely, to avoid lowercasing them. (In practice, almost all archives use uppercase member filenames.) File-writing errors didn't previously cause a non-zero exit value - they do now. Fixed memory leak when not all of an archive file member could be read. Added section on how to use nomarch with Emacs to man page. nomarch-1.4/readhuff.h0100600000175000001440000000047107331251333013465 0ustar rususers/* nomarch 1.0 - extract old `.arc' archives. * Copyright (C) 2001 Russell Marks. See main.c for license details. * * readhuff.h */ extern unsigned char *convert_huff(unsigned char *data_in, unsigned long in_len, unsigned long orig_len); nomarch-1.4/readhuff.c0100600000175000001440000000600507331251337013463 0ustar rususers/* nomarch 1.0 - extract old `.arc' archives. * Copyright (C) 2001 Russell Marks. See main.c for license details. * * readhuff.c - read RLE+Huffman-compressed files (the CP/M `SQ' scheme). */ /* I was originally going to adapt some old GPL'd Huffman code, * but it turns out the format pretty much forces an array-based * implementation. Not that I mind that :-), it just makes it * a bit unusual. So this is from scratch (though it wasn't too hard). */ #include #include #include "readrle.h" #include "readhuff.h" struct huff_node_tag { /* data is stored as a negative `pointer' */ int kids[2]; }; #define READ_WORD(x) (x)=rawinput(),(x)|=(rawinput()<<8) #define VALUE_CONV(x) ((x)^0xffff) #define HUFF_EOF 256 static int bitbox,bitsleft; static unsigned char *data_in_point,*data_in_max; static unsigned char *data_out_point,*data_out_max; static int rawinput(void) { if(data_in_point=nodes) { /* must be corrupt */ free(nodearr); free(data_out); return(NULL); } /* it seems we can't rely on getting the EOF code (even though we * do >95% of the time!), so check for `real' EOF too. * (worth checking in case of corrupt file too, I guess.) */ if((b=bit_input())==-1) { f=VALUE_CONV(HUFF_EOF); break; } f=nodearr[f].kids[b]; } f=VALUE_CONV(f); if(f!=HUFF_EOF) outputrle(f,rawoutput); } while(f!=HUFF_EOF); free(nodearr); return(data_out); } nomarch-1.4/readrle.h0100600000175000001440000000055507331251344013324 0ustar rususers/* nomarch 1.0 - extract old `.arc' archives. * Copyright (C) 2001 Russell Marks. See main.c for license details. * * readrle.h */ extern void outputrle(int chr,void (*outputfunc)(int)); extern unsigned char *convert_rle(unsigned char *data_in, unsigned long in_len, unsigned long orig_len); nomarch-1.4/readrle.c0100600000175000001440000000275107331251351013315 0ustar rususers/* nomarch 1.0 - extract old `.arc' archives. * Copyright (C) 2001 Russell Marks. See main.c for license details. * * readrle.c - read RLE-compressed files. * * Also provides the generic outputrle() for the other RLE-using methods * to use. */ #include #include #include #include #include "readrle.h" static unsigned char *data_in_point,*data_in_max; static unsigned char *data_out_point,*data_out_max; static void rawoutput(int byte) { if(data_out_point * Version 1.4. * README: some minor updates to reflect changes in the last 4 years. :-) * readlzw.c (code_resync): fixed a possible hang with corrupt files. 2002-08-08 Russell Marks * Version 1.3. * main.c: now supports reading of self-extracting archives. (It doesn't support those produced with pkpak, but even pkunpak can't read those.) * main.c (arc_extract_or_test): added support for compression methods 5 and 6, which seem to be along the same lines as v1.0 of the CP/M `crunch' scheme. So I was able to adapt some code from lbrate. This isn't very well tested though - it works on the only test file I have. * main.c (read_file_header): added support for the Acorn Archimedes's variant "Spark" format (including the extra compression method), which is based on the more usual .arc. Any `subdirectories' are extracted as the .arc files they really are. 2002-08-07 Russell Marks * main.c: include unistd.h rather than getopt.h, which was troublesome on some systems. Thanks to Geoff Gibbs for pointing this out (I'm slightly annoyed that I didn't notice it myself, to be honest). 2002-04-15 Russell Marks * Version 1.2. * main.c: added `-p' option, to extract files to stdout. (Enabling this also moves some error-reporting from stdout to stderr.) Thanks to Hilko Bengen for reminding me about this one. 2001-08-30 Russell Marks * main.c (mkdatetimet): DST effects from the mktime() timestamp generation caused some file times to be off by an hour - now fixed. 2001-08-16 Russell Marks * Version 1.1. * Added `extracting multiple archives' section to man page, from lbrate. 2001-08-12 Russell Marks * main.c (usage_help): removed embedded LFs in string. * nomarch.1: added section on how to use nomarch with Emacs. * main.c: added `-U' option to use uppercase archive member filenames - or more precisely, to avoid lowercasing them. (In practice, almost all archives use uppercase member filenames.) 2001-08-08 Russell Marks * main.c (arc_extract_or_test): corrected typo in comment :-) (it does *atime* and mtime, not ctime/mtime!). 2001-08-04 Russell Marks * main.c (arc_extract_or_test): file-writing errors didn't previously cause a non-zero exit value - they do now. 2001-08-03 Russell Marks * main.c (read_file_data): fixed memory leak when not all of an archive file member could be read. 2001-07-30 Russell Marks * Version 1.0. nomarch-1.4/mkinstalldirs0100600000175000001440000000133407016274623014341 0ustar rususers#! /bin/sh # mkinstalldirs --- make directory hierarchy # Author: Noah Friedman # Created: 1993-05-16 # Public domain # $Id: mkinstalldirs,v 1.10 1996/05/03 07:37:52 friedman Exp $ errstatus=0 for file do set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` shift pathcomp= for d do pathcomp="$pathcomp$d" case "$pathcomp" in -* ) pathcomp=./$pathcomp ;; esac if test ! -d "$pathcomp"; then echo "mkdir $pathcomp" 1>&2 mkdir "$pathcomp" || lasterr=$? if test ! -d "$pathcomp"; then errstatus=$lasterr fi fi pathcomp="$pathcomp/" done done exit $errstatus # mkinstalldirs ends here