diff options
-rw-r--r-- | toys/pending/diff.c | 54 | ||||
-rw-r--r-- | toys/posix/patch.c | 34 |
2 files changed, 84 insertions, 4 deletions
diff --git a/toys/pending/diff.c b/toys/pending/diff.c index 2d13d977..d7bb43c4 100644 --- a/toys/pending/diff.c +++ b/toys/pending/diff.c @@ -524,12 +524,64 @@ static int cmp(const void *p1, const void *p2) return strcmp(* (char * const *)p1, * (char * const *)p2); } +// quote and escape filenames that have awkward characters +char *quote_filename(char *filename) +{ + char *to = "abfnrtv\"\\", *from = "\a\b\f\n\r\t\v\"\\"; + char *result, *s, *t; + size_t len = 0; + int quote = 0; + + // calculate memory usage and presence of quotes + for (s = filename; *s; s++) { + if (*s == '\a' || *s == '\b' || *s == '\f' || *s == '\r' || *s == '\v' + || *s == '\n' || *s == '\t' || *s == '"' || *s == '\\') + { + quote = 1; + len += 2; + } else if (*s == ' ') { + quote = 1; + len++; + } else if (*s < 0x20 || *s >= 0x80) { + quote = 1; + len += 4; + } else { + len++; + } + } + + // construct the new string + result = xmalloc(len + (quote ? 2 : 0) + 1); + t = result; + if (quote) *t++ = '"'; + for (s = filename; *s; s++) { + if (*s == '\a' || *s == '\b' || *s == '\f' || *s == '\r' || *s == '\v' + || *s == '\n' || *s == '\t' || *s == '"' || *s == '\\') + { + *t = '\\'; + t[1] = to[strchr(from, *s) - from]; + t += 2; + } else if (*s < 0x20 || *s >= 0x80) { + sprintf(t, "\\%.3o", *s); + t += 4; + } else { + *t++ = *s; + } + } + if (quote) *t++ = '"'; + *t = 0; + return result; +} + static void show_label(char *prefix, char *filename, struct stat *sb) { char date[36]; + char *quoted_file; - printf("%s %s\t%s\n", prefix, filename, + quoted_file = quote_filename(filename); + printf("%s %s\t%s\n", prefix, quoted_file, format_iso_time(date, sizeof(date), &sb->st_mtim)); + free(quoted_file); } static void do_diff(char **files) diff --git a/toys/posix/patch.c b/toys/posix/patch.c index efb15432..4850d5df 100644 --- a/toys/posix/patch.c +++ b/toys/posix/patch.c @@ -247,6 +247,35 @@ done: return TT.state; } +// read a filename that has been quoted or escaped +char *unquote_file(char *filename) { + char *s = filename, *result, *t, *u; + int quote = 0, ch; + + // quoted and escaped filenames are larger than the original + result = xmalloc(strlen(filename) + 1); + t = result; + if (*s == '"') { + s++; + quote = 1; + } + for (; *s && !(quote && *s == '"' && !s[1]); s++) { + // don't accept escape sequences unless the filename is quoted + if (quote && *s == '\\' && s[1]) { + if (s[1] >= '0' && s[1] < '8') { + *t++ = strtoul(s + 1, &u, 8); + s = u - 1; + } else { + ch = unescape(s[1]); + *t++ = ch ? ch : s[1]; + s++; + } + } else *t++ = *s; + } + *t = 0; + return result; +} + // Read a patch file and find hunks, opening/creating/deleting files. // Call apply_one_hunk() on each hunk. @@ -322,13 +351,12 @@ void patch_main(void) finish_oldfile(); // Trim date from end of filename (if any). We don't care. - for (s = patchline+4; *s && (*s!='\t' || !isdigit(s[1])); s++) - if (*s=='\\' && s[1]) s++; + for (s = patchline+4; *s && *s!='\t'; s++); i = atoi(s); if (i>1900 && i<=1970) *name = xstrdup("/dev/null"); else { *s = 0; - *name = xstrdup(patchline+4); + *name = unquote_file(patchline+4); } // We defer actually opening the file because svn produces broken |