quote.c: substitute path_relative with relative_path
authorJiang Xin <worldhello.net@gmail.com>
Tue, 25 Jun 2013 15:53:44 +0000 (23:53 +0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 26 Jun 2013 18:13:50 +0000 (11:13 -0700)
Substitute the function path_relative in quote.c with the function
relative_path. Function relative_path can be treated as an enhanced
and more robust version of path_relative.

Outputs of path_relative and it's replacement (relative_path) are the
same for the following cases:

path prefix output of path_relative output of relative_path
======== ========= ======================= =======================
/a/b/c/ /a/b/ c/ c/
/a/b/c /a/b/ c c
/a/ /a/b/ ../ ../
/ /a/b/ ../../ ../../
/a/c /a/b/ ../c ../c
/x/y /a/b/ ../../x/y ../../x/y
a/b/c/ a/b/ c/ c/
a/ a/b/ ../ ../
x/y a/b/ ../../x/y ../../x/y
/a/b (empty) /a/b /a/b
/a/b (null) /a/b /a/b
a/b (empty) a/b a/b
a/b (null) a/b a/b

But if both of the path and the prefix are the same, or the returned
relative path should be the current directory, the outputs of both
functions are different. Function relative_path returns "./", while
function path_relative returns empty string.

path prefix output of path_relative output of relative_path
======== ========= ======================= =======================
/a/b/ /a/b/ (empty) ./
a/b/ a/b/ (empty) ./
(empty) (null) (empty) ./
(empty) (empty) (empty) ./

But the callers of path_relative can handle such cases, or never
encounter this issue at all, because:

* In function quote_path_relative, if the output of path_relative is
empty, append "./" to it, like:

if (!out->len)
strbuf_addstr(out, "./");

* Another caller is write_name_quoted_relative, which is only used
by builtin/ls-files.c. git-ls-files only show files, so path of
files will never be identical with the prefix of a directory.

The following differences show that path_relative does not handle
extra slashes properly:

path prefix output of path_relative output of relative_path
======== ========= ======================= =======================
/a//b//c/ //a/b// ../../../../a//b//c/ c/
a/b//c a//b ../b//c c

And if prefix has no trailing slash, path_relative does not work
properly either. But since prefix always has a trailing slash, it's
not a problem.

path prefix output of path_relative output of relative_path
======== ========= ======================= =======================
/a/b/c/ /a/b b/c/ c/
/a/b /a/b b ./
/a/b/ /a/b b/ ./
/a /a/b/ ../../a ../
a/b/c/ a/b b/c/ c/
a/b/ a/b b/ ./
a a/b ../a ../
x/y a/b/ ../x/y ../../x/y
a/c a/b c ../c
/a/ /a/b (empty) ../
(empty) /a/b ../../ ./

One tricky part in this conversion is write_name() function in
ls-files.c. It takes a counted string, <name, len>, that is to be
made relative to <prefix, prefix_len> and then quoted. Because
write_name_quoted_relative() still takes these two parameters as
counted string, but ignores the count and treat these two as
NUL-terminated strings, this conversion needs to be audited for its
callers:

- For <name, len>, all three callers of write_name() passes a
NUL-terminated string and its true length, so this patch makes
"len" unused.

- For <prefix, prefix_len>, prefix could be a string that is longer
than empty while prefix_len could be 0 when "--full-name" option
is used. This is fixed by checking prefix_len in write_name()
and calling write_name_quoted_relative() with NULL when
prefix_len is set to 0. Again, this makes "prefix_len" given to
write_name_quoted_relative() unused, without introducing a bug.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/ls-files.c
quote.c
index 22020729cbc35dc5fe11d2a3194fe5994fe67ff9..67e3713cd331dd878f579400c17964ce4c31e9ce 100644 (file)
@@ -48,8 +48,13 @@ static const char *tag_resolve_undo = "";
 
 static void write_name(const char* name, size_t len)
 {
-       write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
-                       line_terminator);
+       /*
+        * With "--full-name", prefix_len=0; write_name_quoted_relative()
+        * ignores prefix_len, so this caller needs to pass empty string
+        * in that case (a NULL is good for "").
+        */
+       write_name_quoted_relative(name, len, prefix_len ? prefix : NULL,
+                       prefix_len, stdout, line_terminator);
 }
 
 static void show_dir_entry(const char *tag, struct dir_entry *ent)
diff --git a/quote.c b/quote.c
index 911229fdf3caffe29a87aea76f38dc50863797d4..64ff3441585780434b7de36ae06a1ce8aa35f44b 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -312,75 +312,24 @@ void write_name_quotedpfx(const char *pfx, size_t pfxlen,
        fputc(terminator, fp);
 }
 
-static const char *path_relative(const char *in, int len,
-                                struct strbuf *sb, const char *prefix,
-                                int prefix_len);
-
 void write_name_quoted_relative(const char *name, size_t len,
                                const char *prefix, size_t prefix_len,
                                FILE *fp, int terminator)
 {
        struct strbuf sb = STRBUF_INIT;
 
-       name = path_relative(name, len, &sb, prefix, prefix_len);
+       name = relative_path(name, prefix, &sb);
        write_name_quoted(name, fp, terminator);
 
        strbuf_release(&sb);
 }
 
-/*
- * Give path as relative to prefix.
- *
- * The strbuf may or may not be used, so do not assume it contains the
- * returned path.
- */
-static const char *path_relative(const char *in, int len,
-                                struct strbuf *sb, const char *prefix,
-                                int prefix_len)
-{
-       int off, i;
-
-       if (len < 0)
-               len = strlen(in);
-       if (prefix_len < 0) {
-               if (prefix)
-                       prefix_len = strlen(prefix);
-               else
-                       prefix_len = 0;
-       }
-
-       off = 0;
-       i = 0;
-       while (i < prefix_len && i < len && prefix[i] == in[i]) {
-               if (prefix[i] == '/')
-                       off = i + 1;
-               i++;
-       }
-       in += off;
-       len -= off;
-
-       if (i >= prefix_len)
-               return in;
-
-       strbuf_reset(sb);
-       strbuf_grow(sb, len);
-
-       while (i < prefix_len) {
-               if (prefix[i] == '/')
-                       strbuf_addstr(sb, "../");
-               i++;
-       }
-       strbuf_add(sb, in, len);
-
-       return sb->buf;
-}
-
 /* quote path as relative to the given prefix */
 char *quote_path_relative(const char *in, int len,
                          struct strbuf *out, const char *prefix)
 {
        struct strbuf sb = STRBUF_INIT;
-       const char *rel = path_relative(in, len, &sb, prefix, -1);
+       const char *rel = relative_path(in, prefix, &sb);
        strbuf_reset(out);
        quote_c_style_counted(rel, strlen(rel), out, NULL, 0);
        strbuf_release(&sb);