Date: Mon, 8 Dec 1997 15:34:16 +0200 (IST) From: Eli Zaretskii To: Esa DOT Peuha AT Helsinki DOT FI cc: DJ Delorie , djgpp-workers AT delorie DOT com Subject: Re: Patches for djtar In-Reply-To: Message-ID: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Precedence: bulk On Tue, 18 Nov 1997, Esa A E Peuha wrote: > Here are patches that will allow djtar to extract .zip files. The following changes correct several problems in DJTAR. They should be applied on top of Esa Peuha's patches that add .zip file support. Most of the changes correct various snafus which predate the .zip support inclusion. In particular, when -o switch was used, the code would sometimes close stdin which would effectively disable the interactive file renaming and sometimes even crash the program. Also, files whose names are reserved by one of the DOS device drivers (such as `prn.txt' or `aux.c') are now automatically renamed even *before* the code tries to open them. Other changes include: - creation of directories whose parents don't exist; - creation of absolute pathnames like "/foo/bar"; - `tarchange.lst' is now created in the current directory rather than in the root; - hard and symbolic links are now correctly identified; - better LFN support; - general cleanup of arbitrary char arrays dimensions. Note that the seemingly large change in the `get_new_name' function is just a reindentation, since the code indentation was left intact after it was copied from an inner block, which made the program structure hard to read. The only changes which are related to the new .zip file support are: - cleanup of directory creation code in epunzip.c, like the fixes done in untar.c; - a better way of detecting a .zip file in `main' function. The latter change was necessary because the original code would treat filenames like `foo/bar.zip/baz.tgz' as a .zip archive. The new test only does that for files whose name ends with a ".zip", and the test is now case-insensitive, so e.g. ".ZIP" and ".Zip" are also okay. Btw, I suggest to modify the code so that it would detect the .zip format automatically based on the file contents. This is more consistent with the previous DJTAR operation whereby it always guessed the format without asking the user and without relying on file name features. *** src/utils/djtar/djtar.c~3 Fri Dec 5 16:58:56 1997 --- src/utils/djtar/djtar.c Fri Dec 5 19:10:22 1997 *************** *** 1,12 **** /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #include #include #include #include #include ! #define NO_LFN (!_use_lfn(".")) #include "oread.h" #include "zread.h" --- 1,15 ---- + /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #include #include + #include + #include #include #include #include ! #define NO_LFN(n) (!_use_lfn(n)) #include "oread.h" #include "zread.h" *************** static void *** 71,78 **** DoNameChanges(char *fname) { FILE *f = fopen(fname, "r"); ! char from[100], to[100]; ! char line[250]; if (f == 0) { perror(fname); --- 74,81 ---- DoNameChanges(char *fname) { FILE *f = fopen(fname, "r"); ! char from[PATH_MAX], to[PATH_MAX]; ! char line[PATH_MAX*2 + 10]; if (f == 0) { perror(fname); *************** DoNameChanges(char *fname) *** 80,86 **** } while (1) { ! fgets(line, 250, f); if (feof(f)) break; to[0] = 0; --- 83,89 ---- } while (1) { ! fgets(line, sizeof(line), f); if (feof(f)) break; to[0] = 0; *************** int *** 131,137 **** change(char *fname, const char *problem, int isadir) { CHANGE *ch; ! char new[200]; char *pos; for (ch=change_root; ch; ch = ch->next) --- 134,140 ---- change(char *fname, const char *problem, int isadir) { CHANGE *ch; ! char new[PATH_MAX]; char *pos; for (ch=change_root; ch; ch = ch->next) *************** change(char *fname, const char *problem, *** 150,156 **** } fprintf(log_out, " %s %s\n new name : ", problem, fname); ! gets(new); if ((strcmp(new, "") == 0) && (isadir == 2)) return 0; --- 153,162 ---- } fprintf(log_out, " %s %s\n new name : ", problem, fname); ! fflush(log_out); ! new[0] = '\0'; ! if (!gets(new)) ! Fatal("EOF while reading stdin"); if ((strcmp(new, "") == 0) && (isadir == 2)) return 0; *************** isadir(char *n) *** 212,225 **** 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; --- 218,231 ---- void do_directories(char *n) { ! char tmp[PATH_MAX]; char *sl; int r; ! sprintf(tmp, "%s", n); n = tmp; for (sl=n; *sl; sl++) { ! if (sl > tmp && (*sl == '/' || *sl == '\\') && sl[-1] != ':') { char save = *sl; *sl = 0; *************** guess_file_type(char *buf, register size *** 255,261 **** /* CR before LF means DOS text file (unless we later see binary characters). */ ! else if (*bp == '\r' && bp[1] == '\n') crlf_seen++; bp++; --- 261,267 ---- /* CR before LF means DOS text file (unless we later see binary characters). */ ! else if (*bp == '\r' && buflen && bp[1] == '\n') crlf_seen++; bp++; *************** guess_file_type(char *buf, register size *** 265,394 **** } /*------------------------------------------------------------------------*/ char * get_new_name(char *name_to_change, int *should_be_written) { char *changed_name, *info; - char new[2048]; /* got to think about LFN's! */ ! /* 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, name_to_change, strlen(only_dir))) *should_be_written = 0; changed_name = get_entry(name_to_change); ! 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; ! } ! } ! } } return changed_name; } /*------------------------------------------------------------------------*/ FILE *log_out = stdout; static char djtarx[] = "djtarx.exe"; static char djtart[] = "djtart.exe"; --- 271,461 ---- } /*------------------------------------------------------------------------*/ + char new[2048]; /* got to think about LFN's! */ char * get_new_name(char *name_to_change, int *should_be_written) { char *changed_name, *info; ! /* 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, name_to_change, strlen(only_dir))) *should_be_written = 0; changed_name = get_entry(name_to_change); ! if (should_be_written && !to_stdout && NO_LFN(changed_name)) ! { ! 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; + } + } + } + } return changed_name; } /*------------------------------------------------------------------------*/ + /* We could have a file in a Unix archive whose name is reserved on + MS-DOS by a device driver. Trying to extract such a file would + fail at best and wedge us at worst. We need to rename such files. */ + void + rename_if_dos_device(char *fn) + { + char *base = strrchr(fn, '/'); + struct stat st_buf; + + /* We don't care about backslashified file names because archives + created on DOS cannot possibly include DOS device names. */ + if (base) + base++; + else + base = fn; + + /* The list of character devices is not constant: it depends on + what device drivers did they install in their CONFIG.SYS. + `stat' will tell us if the basename of the file name is a + character device. */ + if (stat(base, &st_buf) == 0 && S_ISCHR(st_buf.st_mode)) + { + char orig[PATH_MAX]; + + strcpy(orig, fn); + /* Prepend a '_'. */ + memmove(base + 1, base, strlen(base) + 1); + base[0] = '_'; + fprintf(log_out, "[ changing %s to %s ]\n", orig, fn); + } + } + + /*------------------------------------------------------------------------*/ + + void + make_directory(char *dirname) + { + int status; + + do_directories(dirname); /* make sure parent exists */ + do { + if (strcmp(dirname, ".") == 0) + status = 0; /* current dir always exists */ + else if (strcmp(dirname, "..") == 0) + status = !isadir(dirname); /* root might have no parent */ + else + status = mkdir(dirname, 0755); + if (status && (errno==EEXIST)) + { + status = change(dirname, "Duplicate directory name", 2); + continue; + } + if (status) + status = change(dirname, "Unable to create directory", 1); + else + fprintf(log_out, "Making directory %s\n", dirname); + } while (status); + } + + /*------------------------------------------------------------------------*/ + FILE *log_out = stdout; static char djtarx[] = "djtarx.exe"; static char djtart[] = "djtart.exe"; *************** main(int argc, char **argv) *** 421,427 **** switch (argv[i][1]) { case 'n': ! DoNameChanges(argv[++i]); break; case 'v': v_switch = 1; --- 488,494 ---- switch (argv[i][1]) { case 'n': ! DoNameChanges(argv[++i]); break; case 'v': v_switch = 1; *************** main(int argc, char **argv) *** 467,476 **** } for (; i < argc; i++) ! if(strstr(argv[i], ".zip")) epunzip_read(argv[i]); else ! tar_gz_read(argv[i]); if (to_stdout) { --- 534,543 ---- } for (; i < argc; i++) ! if(stricmp(argv[i] + strlen(argv[i]) - 4, ".zip") == 0) epunzip_read(argv[i]); else ! tar_gz_read(argv[i]); if (to_stdout) { *************** main(int argc, char **argv) *** 479,485 **** } else { ! change_file = fopen("/tarchange.lst", "w"); if (change_file != (FILE *)0) { dump_changes(); --- 546,552 ---- } else { ! change_file = fopen("tarchange.lst", "w"); if (change_file != (FILE *)0) { dump_changes(); *** src/utils/djtar/untar.c~0 Fri Dec 5 16:58:56 1997 --- src/utils/djtar/untar.c Fri Dec 5 19:02:30 1997 *************** *** 1,3 **** --- 1,4 ---- + /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #include *************** static struct tm *tm; *** 53,58 **** --- 54,61 ---- static int r; static int skipping; + extern char new[]; + int tarread(char *buf, long buf_size) { *************** tarread(char *buf, long buf_size) *** 87,92 **** --- 90,96 ---- { int head_csum = 0; int i; + size_t nlen; memcpy(&header, buf, sizeof header); if (header.name[0] == 0) *************** tarread(char *buf, long buf_size) *** 128,134 **** 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' : '-'); --- 132,138 ---- fprintf(log_out, "%08lx %6lo ", posn, perm); else fprintf(log_out, "%c%c%c%c ", ! S_ISDIR(perm) ? 'd' : header.flags[0] == '2' ? 'l' : '-', perm & S_IRUSR ? 'r' : '-', perm & S_IWUSR ? 'w' : '-', perm & S_IXUSR ? 'x' : '-'); *************** tarread(char *buf, long buf_size) *** 136,143 **** #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); --- 140,149 ---- #if 0 fprintf(log_out, "(out: %ld)", bytes_out); #endif ! if (header.flags[0] == '2') fprintf(log_out, " -> %s", header.filler); + else if (header.flags[0] == '1') + fprintf(log_out, " link to %s", header.filler); fprintf(log_out, "%s\n", !should_be_written && !list_only ? "\t[ skipped ]" : ""); posn += 512 + ((size+511) & ~511); *************** tarread(char *buf, long buf_size) *** 148,179 **** 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; } --- 154,179 ---- if (should_be_written == 0) { skipping = (size+511) & ~511; + if (!skipping) /* an empty file or a directory */ + { + looking_for_header = 1; + if (buf_size < sizeof header) + return 0; + } continue; } ! else if ((changed_name[nlen=strlen(changed_name)-1] == '/' ! || header.flags[0] == '5') /* '5' flags a directory */ ! && !to_stdout) { ! if (changed_name != new) ! { ! memcpy(new, changed_name, nlen+2); ! changed_name = new; ! } ! if (changed_name[nlen] == '/') ! changed_name[nlen] = 0; ! make_directory(changed_name); looking_for_header = 1; continue; } *************** tarread(char *buf, long buf_size) *** 182,188 **** --- 182,194 ---- open_file: if (!to_stdout) { + if (changed_name != new) + { + memcpy(new, changed_name, nlen+2); + changed_name = new; + } do_directories(changed_name); + rename_if_dos_device(changed_name); r = open(changed_name, O_WRONLY | O_BINARY | O_CREAT | O_EXCL, S_IWRITE | S_IREAD); if (r < 0) *** src/utils/djtar/epunzip.c~0 Fri Dec 5 16:58:56 1997 --- src/utils/djtar/epunzip.c Fri Dec 5 18:58:56 1997 *************** epunzip_read(char *zipfilename) *** 145,169 **** else if(changed_name[strlen(changed_name) - 1] == '/' && !to_stdout) { changed_name[strlen(changed_name) - 1] = 0; ! do ! { ! if(strcmp(changed_name, ".") == 0) ! epoutfile = 0; /* current dir always exists */ ! else if(strcmp(changed_name, "..") == 0) ! epoutfile = !isadir(changed_name); /* root has no parent */ ! else ! epoutfile = mkdir(changed_name, 0); ! if(epoutfile && (errno == EACCES)) ! { ! change(changed_name, "Duplicate directory name", 2); ! continue; ! } ! if(epoutfile) ! epoutfile = change(changed_name, "Unable to create directory", 1); ! else ! fprintf(log_out, "Making directory %s\n", changed_name); ! } ! while(epoutfile); continue; } else --- 145,151 ---- else if(changed_name[strlen(changed_name) - 1] == '/' && !to_stdout) { changed_name[strlen(changed_name) - 1] = 0; ! make_directory(changed_name); continue; } else *************** epunzip_read(char *zipfilename) *** 172,177 **** --- 154,160 ---- if(!to_stdout) { do_directories(changed_name); + rename_if_dos_device(changed_name); epoutfile = open(changed_name, O_WRONLY | O_BINARY | O_CREAT | O_EXCL, S_IWRITE | S_IREAD); *** src/utils/djtar/zread.h~1 Fri Dec 5 16:58:56 1997 --- src/utils/djtar/zread.h Fri Dec 5 18:55:48 1997 *************** extern int isadir (char *); *** 164,169 **** --- 164,171 ---- extern void do_directories(char *); extern File_type guess_file_type(char *, register size_t); extern char* get_new_name(char *, int *); + extern void make_directory(char *); + extern void rename_if_dos_device(char *); /* in untar.c */ extern int tarread (char *, long);