#include #include #include #include #include const char ego[] = "UNTAR 1.0, "__DATE__ " by Ron Aaron"; const char version[] = "1.00"; int redirected = 0; union tarheader { char junk[512]; struct { char name [100]; char mode [8]; char uid [8]; char gid [8]; char size [12]; char mtime[12]; char chksum[8]; char link; char linkname[100]; } header; } th; long adjust(long x) { if (x == 0) return 0; x = x/512 + 1; return x * 512; } static char program_name [ MAXFILE ]; void syntax(void) { fprintf(stderr, "\n%s %s: another fine RonWare product!", program_name, version); fprintf(stderr, "\n"); fprintf(stderr, "\nSyntax: %s [-x] tarfile", program_name); fprintf(stderr, "\n (stdin may be redirected, then 'program_name' isn't needed)"); fprintf(stderr, "\nThis program works on UNIX 'tar' files. Its default action is to list"); fprintf(stderr, "\nthe files contained in the archive."); fprintf(stderr, "\n"); fprintf(stderr, "\nIf the '-x' switch is given, the files will be extracted."); fprintf(stderr, "\n"); fprintf(stderr, "\nWhere this utility differs from MKS tar or other tar programs is that"); fprintf(stderr, "\nit knows about MS-DOS filename limitations and tells you what it's doing"); fprintf(stderr, "\nwith the file names."); fprintf(stderr, "\n"); fprintf(stderr, "\nFor example, an entry in a tar file may have a name like:"); fprintf(stderr, "\n examples.may.92/areallongname.c"); fprintf(stderr, "\n%s converts that to:", program_name); fprintf(stderr, "\n examples.92/areallon.c"); fprintf(stderr, "\nThis program also ignores the checksum info in the tar headers, so it may"); fprintf(stderr, "\nbe used to perform file recovery when normal tar program won't."); exit(3); } void process_section( char *start, char *end ) { char *x, *y; int count; /* section is of the form: dir/ or dir or */ // lowercase it, for consistency for (x = start; x < end; x++) *x = tolower(*x); // get rid of sections like a.b.c for (x=start, count = 0; x 1) { // got a weird one, let's adjust it y = end; while (*y != '.') --y; x = strchr(start,'.'); strcpy(x,y); // from last period to first period end -= (y-x); break; } } // get rid of sections which are too long y = strchr(start, '.'); if (!y) { // no extension... if ((end-start) > 8) // name > 8 chars strcpy(start+8, end); } else if (y 8) { strcpy(start + 8, y); end -= (y-start)-8; } y = strchr(start, '.'); if ((end-y)>4) { // strcpy(y+4, end); strcpy(y+1, end-3); // move last 3 chars } } } char *legalname(char * oldname) { char workbuf[100]; char *p = workbuf; char *q; int x; // make copy of buffer strncpy(workbuf, oldname, 100); // convert illegal characters while (p < (workbuf+100)) { if (*p == ' ') { *p = '\0'; break; } switch (*p) { case ':': case '*': case '?': case '^': case '=': case '+': case '|': case '<': case '>': case ',': *p = '_'; break; case '\\': *p = '/'; break; } ++p; } /* convert names of form: a.b.c to form: a.c */ p = q = workbuf; while (*p) { // find next section to process q = strchr(p, '/'); if (q == NULL) q = p + strlen(p); if (p == q) { ++p; continue; } process_section(p,q); q = strchr(p, '/'); if (q == NULL) q = p + strlen(p); // bump p p = (*q) ? q+1 : q; } return workbuf; } void spin() { fprintf(stderr, "+"); } void makedir(char *name) { // create the directory 'name' if (name[strlen(name)-1] == '/') name[strlen(name)-1] = '\0'; if (mkdir(name) == -1) perror(name); } void unarcfile(FILE *tar, char *name, long size) { long here; FILE *out; #define BUF 8*1024 static char buf[BUF]; int cnt; here = ftell(tar); out = fopen(name, "wb"); if (out == NULL) { // is the problem that there's a path prepended which we don't have? fnsplit(name, NULL, buf, NULL, NULL); makedir(buf); out = fopen(name, "wb"); if (out == NULL) { sprintf(buf,"cannot create file [%s]", name); perror(buf); } } else { while (size) { spin(); fread(buf, BUF, 1, tar); if (cnt == -1) { sprintf(buf, "error in read from [%s]", name); perror(buf); break; } cnt = (BUF > size) ? size : BUF; fwrite(buf, 1, cnt, out); // fputc(fgetc(tar), out); size -= cnt; } fclose(out); } fseek(tar, here, SEEK_SET); } void progname( char *name, char *oname) { fnsplit(oname, NULL, NULL, name, NULL); } void main( int argc, char **argv) { FILE *tar; long size, origin; int x; int extract = 0; char name[100]; progname(program_name , argv[0]); redirected = !isatty(fileno(stdout)); if ((isatty(fileno(stdin))) && (argc != 2) && (argc != 3)) syntax(); else { if (argv[1][0] == '-') { if (argv[1][1] == 'x') ++extract; else syntax(); } if (argc > 3) syntax; // open file if stdin not redirected, else use stdin if (isatty(fileno(stdin))) tar = fopen(argv[--argc], "rb"); else tar = stdin; if (tar == NULL) fprintf(stderr, "\nCannot open file %s", argv[argc]); else { while (!feof(tar)) { fread(&th, 1, sizeof(th), tar); origin = ftell(tar); if (th.header.name[0] == 0 || th.header.name[0] == ' ') break; printf("\n%s: %s, ", (th.header.link == '5') ? "Dir " : "File", th.header.name ); if (redirected) fprintf(stderr, "\n%s: %s, ", (th.header.link == '5') ? "Dir " : "File", th.header.name); size = 0; for (x=0; x<12; x++) { if (th.header.size[x] != ' ' && th.header.size[x] != '\0') { size *= 8; size += th.header.size[x] - '0'; } } printf(" (%ld)", size); strcpy(name, legalname(th.header.name)); printf(" [%s]", name); if (redirected) { fprintf(stderr," (%ld)", size); strcpy(name, legalname(th.header.name)); fprintf(stderr, " [%s]", name); } if (extract) { if (th.header.link == '5') // directory makedir(name); else unarcfile(tar, name, size); } fseek(tar, adjust(size) + origin, SEEK_SET); } if (isatty(fileno(stdin))) fclose(tar); } } }