home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
- /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/stat.h>
- #include <string.h>
- #include <errno.h>
- #include <dos.h>
- #include <time.h>
- #include <io.h>
- #include <crt0.h>
-
- #define NO_LFN (!_use_lfn("."))
-
- #include "oread.h"
- #include "zread.h"
-
- static void
- Fatal(const char *msg)
- {
- fprintf(stderr, "Fatal! %s!\n", msg);
- exit(1);
- }
-
- /*------------------------------------------------------------------------*/
-
- typedef struct CE {
- struct CE *next;
- char *from;
- char *to;
- } CE;
-
- #define HASHSIZE 2048
- #define HASHMASK 2047
- #define HASHBITS 11
- CE *htab[HASHSIZE];
-
- static unsigned long
- hash(unsigned char *cp)
- {
- unsigned long rv = 0;
- while (*cp)
- rv += *cp++;
- while (rv > HASHMASK)
- rv = (rv & HASHMASK) + (rv >> HASHBITS);
- return rv;
- }
-
- static void
- store_entry(char *from, char *to)
- {
- unsigned long h = hash(from);
- CE *ce = (CE *)malloc(sizeof(CE));
- if (ce == 0)
- Fatal("Out of memory");
- ce->from = strdup(from);
- ce->to = strdup(to);
- ce->next = htab[h];
- htab[h] = ce;
- }
-
- static char *
- get_entry(char *from)
- {
- CE *ce;
- for (ce = htab[hash(from)]; ce; ce=ce->next)
- {
- if (strcmp(ce->from, from) == 0)
- return ce->to;
- }
- return from;
- }
-
- static void
- DoNameChanges(char *fname)
- {
- FILE *f = fopen(fname, "r");
- char from[100], to[100];
- char line[250];
- if (f == 0)
- {
- perror(fname);
- exit(1);
- }
- while (1)
- {
- fgets(line, 250, f);
- if (feof(f))
- break;
- to[0] = 0;
- sscanf(line, "%s %s", from, to);
- if (to[0])
- store_entry(from, to);
- }
- fclose(f);
- }
-
- /*------------------------------------------------------------------------*/
-
- FILE *change_file;
-
- int v_switch = 0;
- int dot_switch = 1;
- int text_unix = 0;
- int text_dos = 0;
- int z_switch = 0;
- int to_stdout = 0;
- int to_tty = 0;
- int ignore_csum = 0;
- int list_only = 1;
- char skipped_str[] = "[skipped]";
-
- char *only_dir;
-
- /*------------------------------------------------------------------------*/
-
- typedef struct CHANGE {
- struct CHANGE *next;
- char *old;
- char *new;
- int isdir; /* 0=file, 1=dir, 2=skip */
- } CHANGE;
-
- CHANGE *change_root = 0;
-
- static void
- dump_changes(void)
- {
- CHANGE *c;
- for (c=change_root; c; c=c->next)
- fprintf(change_file, "%s -> %s\n", c->old, c->new);
- }
-
- static int
- change(char *fname, const char *problem, int isadir)
- {
- CHANGE *ch;
- char new[200];
- char *pos;
-
- for (ch=change_root; ch; ch = ch->next)
- if ((strncmp(fname, ch->old, strlen(ch->old)) == 0) && ch->isdir)
- {
- if (ch->isdir == 2)
- {
- fprintf(log_out, " [ skipping %s ]\n", fname);
- return 0;
- }
- /* fprintf(log_out, " [ changing %s to ", fname); */
- sprintf(new, "%s%s", ch->new, fname+strlen(ch->old));
- strcpy(fname, new);
- /* fprintf(log_out, "%s ]\n", fname); */
- return 1;
- }
-
- fprintf(log_out, " %s %s\n new name : ", problem, fname);
- gets(new);
-
- if ((strcmp(new, "") == 0) && (isadir == 2))
- return 0;
- if (isadir) isadir=1;
- ch = (CHANGE *)malloc(sizeof(CHANGE));
- if (ch == 0)
- Fatal("Out of memory");
- ch->next = change_root;
- change_root = ch;
- ch->old = strdup(fname);
- pos = strrchr(fname, '/');
- if (pos && (strchr(new, '/') == 0))
- {
- if (new[0] == 0)
- ch->new = skipped_str;
- else
- {
- ch->new = (char *)malloc(strlen(new) + (pos-fname) + 2);
- if (ch->new == 0)
- Fatal("Out of memory");
- *pos = 0;
- sprintf(ch->new, "%s/%s", fname, new);
- }
- }
- else if (new[0] == 0)
- ch->new = skipped_str;
- else
- ch->new = strdup(new);
- ch->isdir = isadir;
- strcpy(fname, ch->new);
- if (new[0] == 0)
- {
- ch->isdir = 2;
- return 0;
- }
- return 1;
- }
-
- /*------------------------------------------------------------------------*/
-
- static int
- isadir(char *n)
- {
- #ifdef __DJGPP__
- return (access(n, D_OK) == 0);
- #else
- union REGS r;
- struct SREGS s;
- r.x.ax = 0x4300;
- r.x.dx = FP_OFF(n);
- s.ds = FP_SEG(n);
- int86x(0x21, &r, &r, &s);
- if (r.x.flags & 1)
- return 0;
- return r.x.cx & 0x10;
- #endif
- }
-
- static void
- do_directories(char *n)
- {
- char tmp[100];
- char *sl;
- int r;
- sprintf(tmp, n);
- n = tmp;
- for (sl=n; *sl; sl++)
- {
- if ((*sl == '/' || *sl == '\\') && sl[-1] != ':')
- {
- char save = *sl;
- *sl = 0;
- do {
- if (isadir(n))
- break;
- r = mkdir (n, 0777);
- if (r)
- r = change(n, "Unable to create directory", 1);
- } while (r);
- *sl = save;
- }
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- typedef enum { DOS_BINARY, DOS_TEXT, UNIX_TEXT } File_type;
-
- /* Guess DOS file type by looking at its contents. */
- static File_type
- guess_file_type(char *buf, register size_t buflen)
- {
- int crlf_seen = 0;
- /* Use unsigned char, so this will work with foreign characters. */
- register unsigned char *bp = buf;
-
- while (buflen--)
- {
- /* Binary files have characters with ASCII code less then 32 decimal,
- unless they are one of: BS (for man pages), TAB, LF, FF, CR, ^Z. */
- if (*bp < ' ' && !(*bp > 7 && *bp <= '\n') &&
- *bp != '\f' && *bp != '\r' && *bp != '\32')
- return DOS_BINARY;
-
- /* CR before LF means DOS text file (unless we later see
- binary characters). */
- else if (*bp == '\r' && bp[1] == '\n')
- crlf_seen++;
-
- bp++;
- }
-
- return crlf_seen ? DOS_TEXT : UNIX_TEXT;
- }
-
- /*------------------------------------------------------------------------*/
-
- typedef struct {
- char name[100];
- char operm[8];
- char ouid[8];
- char ogid[8];
- char osize[12];
- char otime[12];
- char ocsum[8];
- char flags[1];
- char filler[355];
- } TARREC;
-
- static TARREC header;
- static int looking_for_header;
- static char *changed_name;
- static int first_block = 1;
- static File_type file_type = DOS_BINARY;
- static long perm, uid, gid, size;
- static long posn = 0;
- static time_t ftime;
- static struct ftime ftimes;
- static struct tm *tm;
- static int r;
- static int skipping;
- static char new[2048]; /* got to think about LFN's! */
-
- int
- tarread(char *buf, long buf_size)
- {
- int should_be_written;
-
- while (buf_size)
- {
- char *info;
- int write_errno = 0;
- int dsize = 512, wsize;
-
- if (skipping)
- {
- if (skipping <= buf_size)
- {
- bytes_out += skipping;
- buf += skipping;
- buf_size -= skipping;
- skipping = 0;
- looking_for_header = 1;
- if (buf_size < sizeof header)
- return 0;
- }
- else
- {
- bytes_out += buf_size;
- skipping -= buf_size;
- return 0;
- }
- }
-
- if (looking_for_header)
- {
- int head_csum = 0;
- int i;
-
- memcpy(&header, buf, sizeof header);
- if (header.name[0] == 0)
- {
- bytes_out += buf_size; /* assume everything left should be counted */
- return EOF;
- }
- buf += sizeof header;
- buf_size -= sizeof header;
- bytes_out += sizeof header;
- first_block = 1;
- file_type = DOS_BINARY;
- looking_for_header = 0;
-
- /* ONLY_DIR says to extract only files which are siblings
- of that directory. */
- should_be_written = list_only == 0;
- if (should_be_written &&
- only_dir && strncmp(only_dir, header.name, strlen(only_dir)))
- should_be_written = 0;
- sscanf(header.operm, " %lo", &perm);
- sscanf(header.ouid, " %lo", &uid);
- sscanf(header.ogid, " %lo", &gid);
- sscanf(header.osize, " %lo", &size);
- sscanf(header.otime, " %o", &ftime);
- sscanf(header.ocsum, " %o", &head_csum);
- for (i = 0; i < sizeof header; i++)
- {
- /* Checksum on header, but with the checksum field blanked out. */
- int j = (i > 147 && i < 156) ? ' ' : *((unsigned char *)&header + i);
-
- head_csum -= j;
- }
- if (head_csum && !ignore_csum)
- {
- /* Probably corrupted archive. Bail out. */
- fprintf(log_out, "--- !!Directory checksum error!! ---\n");
- bytes_out += buf_size;
- return EOF;
- }
-
- changed_name = get_entry(header.name);
- if (should_be_written && !to_stdout && NO_LFN)
- {
- info = strstr(changed_name, ".info-");
- if (info)
- {
- strcpy(new, changed_name);
- info = strstr(new, ".info-");
- strcpy(info+2, info+6);
- fprintf(log_out, "[ changing %s to %s ]\n", changed_name, new);
- }
- else
- {
- char *tgz = strstr(changed_name, ".tar.gz");
- if (tgz)
- {
- strcpy(new, changed_name);
- tgz = strstr(new, ".tar.gz");
- strcpy(tgz, ".tgz");
- strcat(tgz, tgz+7);
- fprintf(log_out, "[ changing %s to %s ]\n", changed_name, new);
- }
- else
- {
- char *plus = strstr(changed_name, "++"), *plus2;
- if (plus)
- {
- strcpy(new, changed_name);
- plus2 = strstr(new, "++");
- strcpy(plus2, "plus");
- strcpy(plus2+4, plus+2);
- fprintf(log_out, "[ changing %s to %s ]\n", changed_name, new);
- }
- else
- {
- strcpy(new, changed_name);
- }
- }
- }
- changed_name = new;
-
- if (dot_switch)
- {
- char *p = changed_name, *name_start = changed_name;
- int state = 0;
- /* 0 = start of item
- 1 = before dot (not counting initial dot), but not first char
- 2 = after first dot */
-
- /* ".gdbinit" -> "_gdbinit"
- "emacs-19.28.90/ChangeLog" -> "emacs-19.28-90/ChangeLog"
- "./dir/file" -> "./dir/file"
- "sh.lex.c" -> "sh_lex.c"
- */
- while (*p)
- {
- switch (*p++)
- {
- case '/':
- case '\\':
- state = 0;
- name_start = p;
- break;
- case '.':
- /* Don't waste our limited 8-char real estate in the
- name part too early, unless the name is really short. */
- if (state == 1 && p - name_start < 5)
- {
- size_t namelen = strlen(p);
- char *next_slash = memchr(p, '/', namelen);
- char *next_bslash = memchr(p, '\\', namelen);
- char *next_dot = memchr(p, '.', namelen);
-
- /* Find where this name ends. */
- if (next_slash == (char *)0)
- {
- if (next_bslash)
- next_slash = next_bslash;
- else
- next_slash = p + namelen;
- }
-
- else if (next_bslash && next_bslash < next_slash)
- next_slash = next_bslash;
-
- /* If name has more dots, convert this one to `_'. */
- if (next_dot && next_dot < next_slash)
- {
- p[-1] = '_';
- break; /* don't bump `state' */
- }
- }
-
- /* Leave "./", "../", "/." etc. alone. */
- if (state != 0 ||
- (*p && *p != '/' && *p != '\\' && *p != '.'))
- p[-1] = "_.-"[state];
- if (state < 2) state++;
- break;
- default:
- if (state == 0) state = 1;
- }
- }
- }
- }
-
- if (v_switch)
- fprintf(log_out, "%08lx %6lo ", posn, perm);
- else
- fprintf(log_out, "%c%c%c%c ",
- S_ISDIR(perm) ? 'd' : '-',
- perm & S_IRUSR ? 'r' : '-',
- perm & S_IWUSR ? 'w' : '-',
- perm & S_IXUSR ? 'x' : '-');
- fprintf(log_out, "%.20s %9ld %s", ctime(&ftime)+4, size, changed_name);
- #if 0
- fprintf(log_out, "(out: %ld)", bytes_out);
- #endif
- if (header.flags[1] == 0x32)
- fprintf(log_out, " -> %s", header.filler);
- fprintf(log_out, "%s\n",
- !should_be_written && !list_only ? "\t[ skipped ]" : "");
- posn += 512 + ((size+511) & ~511);
- #if 0
- fprintf(log_out, "%6lo %02x %12ld %s\n",perm,header.flags[0],size,changed_name);
- #endif
- if (should_be_written == 0)
- {
- skipping = (size+511) & ~511;
- continue;
- }
- else if (changed_name[strlen(changed_name)-1] == '/' && !to_stdout)
- {
- changed_name[strlen(changed_name)-1] = 0;
- do {
- if (strcmp(changed_name, ".") == 0)
- r = 0; /* current dir always exists */
- else if (strcmp(changed_name, "..") == 0)
- r = !isadir(changed_name); /* root has no parent */
- else
- r = mkdir(changed_name
- #ifdef __GO32__
- ,0
- #endif
- );
- if (r && (errno==EACCES))
- {
- change(changed_name, "Duplicate directory name", 2);
- continue;
- }
- if (r)
- r = change(changed_name, "Unable to create directory", 1);
- else
- fprintf(log_out, "Making directory %s\n", changed_name);
- } while (r);
- looking_for_header = 1;
- continue;
- }
- else
- {
- open_file:
- if (!to_stdout)
- {
- do_directories(changed_name);
- r = open(changed_name,
- O_WRONLY | O_BINARY | O_CREAT | O_EXCL, S_IWRITE | S_IREAD);
- if (r < 0)
- if (change(changed_name, "Cannot exclusively open file", 0))
- goto open_file;
- else
- {
- skipping = (size+511) & ~511;
- continue;
- }
- }
- else
- {
- r = fileno(stdout);
- if (!to_tty)
- setmode(r, O_BINARY);
- }
- }
- }
-
- while (size)
- {
- char tbuf[512];
- char *wbuf = buf;
-
- if (buf_size <= 0) /* this buffer exhausted */
- return 0;
- if (size < 512)
- dsize = size;
- else if (buf_size < 512)
- dsize = buf_size;
- else
- dsize = 512;
- if (first_block && (text_dos || text_unix || to_tty))
- {
- file_type = guess_file_type(buf, dsize);
- first_block = 0;
- if (file_type == UNIX_TEXT && text_dos)
- setmode(r, O_TEXT); /* will add CR chars to each line */
- }
- if ((text_unix || to_tty) && file_type == DOS_TEXT)
- {
- /* If they asked for text files to be written Unix style, or
- we are writing to console, remove the CR and ^Z characters
- from DOS text files.
- Note that we don't alter the original uncompressed data so
- as not to screw up the CRC computations. */
- char *s=buf, *d=tbuf;
- while (s-buf < dsize)
- {
- if (*s != '\r' && *s != 26)
- *d++ = *s;
- s++;
- }
- wsize = d - tbuf;
- wbuf = tbuf;
- }
- else
- {
- wbuf = buf;
- wsize = dsize;
- }
- errno = 0;
- if (write(r, wbuf, wsize) < wsize)
- {
- if (errno == 0)
- errno = ENOSPC;
- fprintf(log_out, "%s: %s\n", changed_name, strerror(errno));
- write_errno = errno;
- break;
- }
- size -= dsize;
- buf_size -= dsize;
- buf += dsize;
- bytes_out += dsize;
- }
-
- if (!to_stdout)
- {
- close(r);
- r = open(changed_name, O_RDONLY);
- tm = localtime(&ftime);
- ftimes.ft_tsec = tm->tm_sec / 2;
- ftimes.ft_min = tm->tm_min;
- ftimes.ft_hour = tm->tm_hour;
- ftimes.ft_day = tm->tm_mday;
- ftimes.ft_month = tm->tm_mon+1;
- ftimes.ft_year = tm->tm_year - 80;
- setftime(r, &ftimes);
- close(r);
- chmod(changed_name, perm);
- }
- looking_for_header = 1;
- if (write_errno == ENOSPC) /* target disk full: quit early */
- {
- bytes_out += buf_size;
- return EOF;
- }
- else if (write_errno) /* other error: skip this file, try next */
- skipping = (size - dsize + 511) & ~511;
- else /* skip the slack garbage to the next 512-byte boundary */
- skipping = 512 - dsize;
- }
- return 0;
- }
-
- /*------------------------------------------------------------------------*/
-
- int part_nb;
- static const char *zip_description[] = {
- "uncompressed",
- "COMPRESS'ed",
- "PACK'ed",
- "LZH'ed",
- "compressed by unknown method (4)",
- "compressed by unknown method (5)",
- "compressed by unknown method (6)",
- "compressed by unknown method (7)",
- "deflated by "
- };
-
- static void
- tar_gz_read(char *fname)
- {
- void *f;
-
- errno = 0;
- f = oread_open(fname);
- if (errno)
- {
- fprintf(log_out, "%s: %s\n", fname, strerror(errno));
- return;
- }
- ifname = fname;
- header_bytes = 0;
- clear_bufs(); /* clear input and output buffers */
- part_nb = 0; /* FIXME!! handle multi-part gzip's */
- method = get_method(f);
- if (method < 0)
- {
- oread_close(f);
- return; /* error message already emitted */
- }
- if (v_switch)
- fprintf(log_out, "-- `%s\' is %s%s --\n\n",
- fname,
- method > MAX_METHODS ? "corrupted (?)" : zip_description[method],
- method == DEFLATED ? (pkzip ? "PKZip" : "GZip") : "");
-
- bytes_out = 0;
- looking_for_header = 1;
- posn = 0;
-
- if ((*decompressor)(f) != OK)
- {
- fprintf(log_out,
- "\n%s: corrupted file; I might have written garbage\n", fname);
- fflush(log_out);
- }
- oread_close(f);
- method = -1;
- }
-
- /*------------------------------------------------------------------------*/
-
- FILE * log_out = stdout;
- static char djtarx[] = "djtarx.exe";
- static char djtart[] = "djtart.exe";
-
- int
- main(int argc, char **argv)
- {
- int i = 1;
- char *tp;
- char *xp;
-
- progname = strlwr(strdup(argv[0]));
-
- if (argc < 2)
- {
- fprintf(stderr, "Usage: %s [-n changeFile] [-p] [-i] [-t|x] [-o dir] [-v] [-u|d|b] [-[!].] tarfile...\n", progname);
- exit(1);
- }
-
- /* DJTARX -> ``djtar -x'', DJTART -> ``djtar -t''. */
- tp = strstr(progname, djtart);
- xp = strstr(progname, djtarx);
- /* Check both with and without .exe, just in case. */
- if (tp && (tp[sizeof(djtart)-1] == '\0' || tp[sizeof(djtart)-5] == '\0'))
- list_only = 1;
- else if (xp && (xp[sizeof(djtarx)-1] == '\0' || xp[sizeof(djtarx)-5] == '\0'))
- list_only = 0;
- while ((argc > i) && (argv[i][0] == '-') && argv[i][1])
- {
- switch (argv[i][1])
- {
- case 'n':
- DoNameChanges(argv[i+1]);
- i++;
- break;
- case 'v':
- v_switch = 1;
- break;
- case 'u':
- text_unix = 1;
- text_dos = 0;
- break;
- case 'd':
- text_dos = 1;
- text_unix = 0;
- break;
- case 'b':
- text_dos = 0;
- text_unix = 0;
- break;
- case '.':
- dot_switch = 1;
- break;
- case '!':
- if (argv[i][2] == '.')
- dot_switch = 0;
- break;
- case 'o':
- only_dir = strdup(argv[++i]);
- break;
- case 'p':
- to_stdout = 1;
- to_tty = isatty(fileno(stdout));
- log_out = stderr;
- text_dos = 1;
- break;
- case 't':
- list_only = 1;
- break;
- case 'i':
- ignore_csum = 1;
- break;
- case 'x':
- list_only = 0;
- }
- i++;
- }
-
- for (; i < argc; i++)
- tar_gz_read(argv[i]);
-
- if (to_stdout)
- {
- setmode(fileno(stdout), O_TEXT);
- return 0;
- }
- else
- {
- change_file = fopen("/tarchange.lst", "w");
- if (change_file != (FILE *)0)
- {
- dump_changes();
- fclose(change_file);
- return 0;
- }
- else
- return 1;
- }
- }
-