| #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; | 
 | } |