|  | #include "cache.h" | 
|  | #include "chdir-notify.h" | 
|  | #include "list.h" | 
|  | #include "strbuf.h" | 
|  |  | 
|  | struct chdir_notify_entry { | 
|  | const char *name; | 
|  | chdir_notify_callback cb; | 
|  | void *data; | 
|  | struct list_head list; | 
|  | }; | 
|  | static LIST_HEAD(chdir_notify_entries); | 
|  |  | 
|  | void chdir_notify_register(const char *name, | 
|  | chdir_notify_callback cb, | 
|  | void *data) | 
|  | { | 
|  | struct chdir_notify_entry *e = xmalloc(sizeof(*e)); | 
|  | e->name = name; | 
|  | e->cb = cb; | 
|  | e->data = data; | 
|  | list_add_tail(&e->list, &chdir_notify_entries); | 
|  | } | 
|  |  | 
|  | static void reparent_cb(const char *name, | 
|  | const char *old_cwd, | 
|  | const char *new_cwd, | 
|  | void *data) | 
|  | { | 
|  | char **path = data; | 
|  | char *tmp = *path; | 
|  |  | 
|  | if (!tmp) | 
|  | return; | 
|  |  | 
|  | *path = reparent_relative_path(old_cwd, new_cwd, tmp); | 
|  | free(tmp); | 
|  |  | 
|  | if (name) { | 
|  | trace_printf_key(&trace_setup_key, | 
|  | "setup: reparent %s to '%s'", | 
|  | name, *path); | 
|  | } | 
|  | } | 
|  |  | 
|  | void chdir_notify_reparent(const char *name, char **path) | 
|  | { | 
|  | chdir_notify_register(name, reparent_cb, path); | 
|  | } | 
|  |  | 
|  | int chdir_notify(const char *new_cwd) | 
|  | { | 
|  | struct strbuf old_cwd = STRBUF_INIT; | 
|  | struct list_head *pos; | 
|  |  | 
|  | if (strbuf_getcwd(&old_cwd) < 0) | 
|  | return -1; | 
|  | if (chdir(new_cwd) < 0) { | 
|  | int saved_errno = errno; | 
|  | strbuf_release(&old_cwd); | 
|  | errno = saved_errno; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | trace_printf_key(&trace_setup_key, | 
|  | "setup: chdir from '%s' to '%s'", | 
|  | old_cwd.buf, new_cwd); | 
|  |  | 
|  | list_for_each(pos, &chdir_notify_entries) { | 
|  | struct chdir_notify_entry *e = | 
|  | list_entry(pos, struct chdir_notify_entry, list); | 
|  | e->cb(e->name, old_cwd.buf, new_cwd, e->data); | 
|  | } | 
|  |  | 
|  | strbuf_release(&old_cwd); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | char *reparent_relative_path(const char *old_cwd, | 
|  | const char *new_cwd, | 
|  | const char *path) | 
|  | { | 
|  | char *ret, *full; | 
|  |  | 
|  | if (is_absolute_path(path)) | 
|  | return xstrdup(path); | 
|  |  | 
|  | full = xstrfmt("%s/%s", old_cwd, path); | 
|  | ret = xstrdup(remove_leading_path(full, new_cwd)); | 
|  | free(full); | 
|  |  | 
|  | return ret; | 
|  | } |