Merge branch 'nd/submodule-status-fix'
[git] / chdir-notify.c
1 #include "cache.h"
2 #include "chdir-notify.h"
3 #include "list.h"
4 #include "strbuf.h"
5
6 struct chdir_notify_entry {
7         const char *name;
8         chdir_notify_callback cb;
9         void *data;
10         struct list_head list;
11 };
12 static LIST_HEAD(chdir_notify_entries);
13
14 void chdir_notify_register(const char *name,
15                            chdir_notify_callback cb,
16                            void *data)
17 {
18         struct chdir_notify_entry *e = xmalloc(sizeof(*e));
19         e->name = name;
20         e->cb = cb;
21         e->data = data;
22         list_add_tail(&e->list, &chdir_notify_entries);
23 }
24
25 static void reparent_cb(const char *name,
26                         const char *old_cwd,
27                         const char *new_cwd,
28                         void *data)
29 {
30         char **path = data;
31         char *tmp = *path;
32
33         if (!tmp)
34                 return;
35
36         *path = reparent_relative_path(old_cwd, new_cwd, tmp);
37         free(tmp);
38
39         if (name) {
40                 trace_printf_key(&trace_setup_key,
41                                  "setup: reparent %s to '%s'",
42                                  name, *path);
43         }
44 }
45
46 void chdir_notify_reparent(const char *name, char **path)
47 {
48         chdir_notify_register(name, reparent_cb, path);
49 }
50
51 int chdir_notify(const char *new_cwd)
52 {
53         struct strbuf old_cwd = STRBUF_INIT;
54         struct list_head *pos;
55
56         if (strbuf_getcwd(&old_cwd) < 0)
57                 return -1;
58         if (chdir(new_cwd) < 0) {
59                 int saved_errno = errno;
60                 strbuf_release(&old_cwd);
61                 errno = saved_errno;
62                 return -1;
63         }
64
65         trace_printf_key(&trace_setup_key,
66                          "setup: chdir from '%s' to '%s'",
67                          old_cwd.buf, new_cwd);
68
69         list_for_each(pos, &chdir_notify_entries) {
70                 struct chdir_notify_entry *e =
71                         list_entry(pos, struct chdir_notify_entry, list);
72                 e->cb(e->name, old_cwd.buf, new_cwd, e->data);
73         }
74
75         strbuf_release(&old_cwd);
76         return 0;
77 }
78
79 char *reparent_relative_path(const char *old_cwd,
80                              const char *new_cwd,
81                              const char *path)
82 {
83         char *ret, *full;
84
85         if (is_absolute_path(path))
86                 return xstrdup(path);
87
88         full = xstrfmt("%s/%s", old_cwd, path);
89         ret = xstrdup(remove_leading_path(full, new_cwd));
90         free(full);
91
92         return ret;
93 }