new my scripts/mm_mkdep, dependences work now
diff --git a/scripts/bb_mkdep.c b/scripts/bb_mkdep.c
new file mode 100644
index 0000000..4083973
--- /dev/null
+++ b/scripts/bb_mkdep.c
@@ -0,0 +1,855 @@
+/*
+ * Another dependences for Makefile mashine generator
+ *
+ * Copyright (C) 2005 by Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * This programm do
+ * 1) find #define KEY VALUE or #undef KEY from include/config.h
+ * 2) save include/config/key*.h if changed after previous usage
+ * 3) recursive scan from "./" *.[ch] files, but skip scan include/config/...
+ * 4) find #include "*.h" and KEYs using, if not as #define and #undef
+ * 5) generate depend to stdout
+ *    path/file.o: include/config/key*.h found_include_*.h
+ *    path/inc.h: include/config/key*.h found_included_include_*.h
+ * This programm do not generate dependences for #include <...>
+ *
+ * Options:
+ * -I local_include_path  (include`s paths, default: LOCAL_INCLUDE_PATH)
+ * -d                     (don`t generate depend)
+ * -w                     (show warning if include files not found)
+ * -k include/config      (default: INCLUDE_CONFIG_PATH)
+ * -c include/config.h    (configs, default: INCLUDE_CONFIG_KEYS_PATH)
+*/
+
+#define LOCAL_INCLUDE_PATH          "include"
+#define INCLUDE_CONFIG_PATH         LOCAL_INCLUDE_PATH"/config"
+#define INCLUDE_CONFIG_KEYS_PATH    LOCAL_INCLUDE_PATH"/config.h"
+
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+typedef struct BB_KEYS {
+	char *keyname;
+	const char *value;
+	char *stored_path;
+	int  checked;
+	struct BB_KEYS *next;
+} bb_key_t;
+
+
+/* partial and simplify libbb routine */
+
+void bb_error_d(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
+char * bb_asprint(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
+
+/* stolen from libbb as is */
+typedef struct llist_s {
+	char *data;
+	struct llist_s *link;
+} llist_t;
+llist_t *llist_add_to(llist_t *old_head, char *new_item);
+void *xrealloc(void *p, size_t size);
+void *xmalloc(size_t size);
+char *bb_xstrdup(const char *s);
+char *bb_simplify_path(const char *path);
+
+/* for lexical analyzier */
+static bb_key_t *key_top;
+
+static void parse_inc(const char *include, const char *fname);
+static void parse_conf_opt(char *opt, const char *val, size_t rsz);
+
+#define CHECK_ONLY  0
+#define MAKE_NEW    1
+static bb_key_t *find_already(bb_key_t *k, const char *nk, int flg_save_new);
+
+#define yy_error_d(s) bb_error_d("%s:%d hmm, %s", fname, line, s)
+
+/* state */
+#define S      0        /* start state */
+#define STR    '"'      /* string */
+#define CHR    '\''     /* char */
+#define REM    '*'      /* block comment */
+#define POUND  '#'      /* # */
+#define I      'i'      /* #include preprocessor`s directive */
+#define D      'd'      /* #define preprocessor`s directive */
+#define U      'u'      /* #undef preprocessor`s directive */
+#define LI     'I'      /* #include "... */
+#define DK     'K'      /* #define KEY... (config mode) */
+#define DV     'V'      /* #define KEY "... or #define KEY '... */
+#define NLC    'n'      /* \+\n */
+#define ANY    '?'      /* skip unparsed . */
+
+/* [A-Z_a-z] */
+#define ID(c) ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
+/* [A-Z_a-z0-9] */
+#define ISALNUM(c)  (ID(c) || (c >= '0' && c <= '9'))
+
+#define getc1()     do { c = (optr >= oend) ? EOF : *optr++; } while(0)
+#define ungetc1()   optr--
+
+#define put_id(c)   do {    if(id_len == mema_id)                 \
+				id = xrealloc(id, mema_id += 16); \
+			    id[id_len++] = c; } while(0)
+
+/* stupid C lexical analizator */
+static void c_lex(const char *fname, int flg_config_include)
+{
+  int c = EOF;                      /* stupid initialize */
+  int prev_state = EOF;
+  int called;
+  int state;
+  int line;
+  static size_t mema_id;
+  char *id = xmalloc(mema_id=128);  /* fist allocate */
+  size_t id_len = 0;                /* stupid initialize */
+  char *val = NULL;
+  unsigned char *optr, *oend;
+  unsigned char *start = NULL;      /* stupid initialize */
+
+  int fd;
+  char *map;
+  int mapsize;
+  {
+    /* stolen from mkdep by Linus Torvalds */
+    int pagesizem1 = getpagesize() - 1;
+    struct stat st;
+
+    fd = open(fname, O_RDONLY);
+    if(fd < 0) {
+	perror(fname);
+	return;
+    }
+    fstat(fd, &st);
+    if (st.st_size == 0)
+	bb_error_d("%s is empty", fname);
+    mapsize = st.st_size;
+    mapsize = (mapsize+pagesizem1) & ~pagesizem1;
+    map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0);
+    if ((long) map == -1)
+	bb_error_d("%s: mmap: %m", fname);
+
+    /* hereinafter is my */
+    optr = (unsigned char *)map;
+    oend = optr + st.st_size;
+  }
+
+  line = 1;
+  called = state = S;
+
+  for(;;) {
+	if(state == LI || state == DV) {
+	    /* store "include.h" or config mode #define KEY "|'..."|'  */
+	    put_id(0);
+	    if(state == LI) {
+		parse_inc(id, fname);
+	    } else {
+		/*
+		if(val[0] == '\0')
+		    yy_error_d("expected value");
+		*/
+		parse_conf_opt(id, val, (optr - start));
+	    }
+	    state = S;
+	}
+	if(prev_state != state) {
+	    prev_state = state;
+	    getc1();
+	}
+
+	/* [ \t]+ eat first space */
+	while(c == ' ' || c == '\t')
+	    getc1();
+
+	if(c == '\\') {
+		getc1();
+		if(c == '\n') {
+			/* \\\n eat continued */
+			line++;
+			prev_state = NLC;
+			continue;
+		}
+		ungetc1();
+		c = '\\';
+	}
+
+	if(state == S) {
+		while(c <= ' ' && c != EOF) {
+		    /* <S>[\000- ]+ */
+		    if(c == '\n')
+			line++;
+		    getc1();
+		}
+		if(c == EOF) {
+			/* <S><<EOF>> */
+			munmap(map, mapsize);
+			close(fd);
+			return;
+		}
+		if(c == '/') {
+			/* <S>/ */
+			getc1();
+			if(c == '/') {
+				/* <S>"//"[^\n]* */
+				do getc1(); while(c != '\n' && c != EOF);
+			} else if(c == '*') {
+				/* <S>[/][*] */
+				called = S;
+				state = REM;
+			}
+			/* eat <S>/ */
+		} else if(c == '#') {
+			/* <S>\"|\'|# */
+			start = optr - 1;
+			state = c;
+		} else if(c == STR || c == CHR) {
+			/* <S>\"|\'|# */
+			val = NULL;
+			called = S;
+			state = c;
+		} else if(ISALNUM(c)) {
+			/* <S>[A-Z_a-z0-9] */
+			id_len = 0;
+			do {
+				/* <S>[A-Z_a-z0-9]+ */
+				put_id(c);
+				getc1();
+			} while(ISALNUM(c));
+			put_id(0);
+			find_already(key_top, id, CHECK_ONLY);
+		} else {
+		    /* <S>. */
+		    prev_state = ANY;
+		}
+		continue;
+	}
+	if(state == REM) {
+	  for(;;) {
+		/* <REM>[^*]+ */
+		while(c != '*') {
+			if(c == '\n') {
+				/* <REM>\n */
+				if(called != S)
+				    yy_error_d("unexpected newline");
+				line++;
+			} else if(c == EOF)
+				yy_error_d("unexpected EOF");
+			getc1();
+		}
+		/* <REM>[*] */
+		getc1();
+		if(c == '/') {
+			/* <REM>[*][/] */
+			state = called;
+			break;
+		}
+	  }
+	  continue;
+	}
+	if(state == STR || state == CHR) {
+	    for(;;) {
+		/* <STR,CHR>\n|<<EOF>> */
+		if(c == '\n' || c == EOF)
+			yy_error_d("unterminating");
+		if(c == '\\') {
+			/* <STR,CHR>\\ */
+			getc1();
+			if(c != '\\' && c != '\n' && c != state) {
+			    /* another usage \ in str or char */
+			    if(c == EOF)
+				yy_error_d("unexpected EOF");
+			    if(val)
+				put_id(c);
+			    continue;
+			}
+			/* <STR,CHR>\\[\\\n] or <STR>\\\" or <CHR>\\\' */
+			/* eat 2 char */
+			if(c == '\n')
+			    line++;
+			else if(val)
+			    put_id(c);
+		} else if(c == state) {
+			/* <STR>\" or <CHR>\' */
+			if(called == DV)
+			    put_id(c);
+			state = called;
+			break;
+		} else if(val)
+			put_id(c);
+		/* <STR,CHR>. */
+		getc1();
+	    }
+	    continue;
+	}
+
+	/* begin preprocessor states */
+	if(c == EOF)
+	    yy_error_d("unexpected EOF");
+	if(c == '/') {
+		/* <#.*>/ */
+		getc1();
+		if(c == '/')
+			yy_error_d("detect // in preprocessor line");
+		if(c == '*') {
+			/* <#.*>[/][*] */
+			called = state;
+			state = REM;
+			continue;
+		}
+		/* hmm, #.*[/] */
+		yy_error_d("strange preprocessor line");
+	}
+	if(state == '#') {
+		static const char * const preproc[] = {
+		    "define", "undef", "include", ""
+		};
+		const char * const *str_type;
+
+		id_len = 0;
+		while(ISALNUM(c)) {
+		    put_id(c);
+		    getc1();
+		}
+		put_id(0);
+		for(str_type = preproc; (state = **str_type); str_type++) {
+		    if(*id == state && strcmp(id, *str_type) == 0)
+			break;
+		}
+		/* to S if another #directive */
+		ungetc1();
+		id_len = 0; /* common for save */
+		continue;
+	}
+	if(state == I) {
+		if(c == STR) {
+			/* <I>\" */
+			val = id;
+			state = STR;
+			called = LI;
+			continue;
+		}
+		/* another (may be wrong) #include ... */
+		ungetc1();
+		state = S;
+		continue;
+	}
+	if(state == D || state == U) {
+	    while(ISALNUM(c)) {
+		if(flg_config_include) {
+		    /* save KEY from #"define"|"undef" ... */
+		    put_id(c);
+		}
+		getc1();
+	    }
+	    if(!flg_config_include) {
+		state = S;
+	    } else {
+		if(!id_len)
+		    yy_error_d("expected identificator");
+		put_id(0);
+		if(state == U) {
+		    parse_conf_opt(id, NULL, (optr - start));
+		    state = S;
+		} else {
+		    /* D -> DK */
+		    state = DK;
+		}
+	    }
+	    ungetc1();
+	    continue;
+	}
+	if(state == DK) {
+	    /* #define (config mode) */
+	    val = id + id_len;
+	    if(c == STR || c == CHR) {
+		/* define KEY "... or define KEY '... */
+		put_id(c);
+		called = DV;
+		state = c;
+		continue;
+	    }
+	    while(ISALNUM(c)) {
+		put_id(c);
+		getc1();
+	    }
+	    ungetc1();
+	    state = DV;
+	    continue;
+	}
+    }
+}
+
+
+static void show_usage(void) __attribute__ ((noreturn));
+static void show_usage(void)
+{
+	bb_error_d("Usage: [-I local_include_path] [-dw] "
+			"[-k path_for_store_keys] [-s skip_file]");
+}
+
+static const char *kp;
+static llist_t *Iop;
+static bb_key_t *Ifound;
+static int noiwarning;
+static llist_t *configs;
+
+static bb_key_t *find_already(bb_key_t *k, const char *nk, int flg_save_new)
+{
+	bb_key_t *cur;
+
+	for(cur = k; cur; cur = cur->next) {
+	    if(strcmp(cur->keyname, nk) == 0) {
+		cur->checked = 1;
+		return NULL;
+	    }
+	}
+	if(flg_save_new == CHECK_ONLY)
+	    return NULL;
+	cur = xmalloc(sizeof(bb_key_t));
+	cur->keyname = bb_xstrdup(nk);
+	cur->checked = 1;
+	cur->next = k;
+	return cur;
+}
+
+static int store_include_fullpath(char *p_i, bb_key_t *li)
+{
+    struct stat st;
+    int ok = 0;
+
+    if(stat(p_i, &st) == 0) {
+	li->stored_path = bb_simplify_path(p_i);
+	ok = 1;
+    }
+    free(p_i);
+    return ok;
+}
+
+static void parse_inc(const char *include, const char *fname)
+{
+	bb_key_t *li;
+	char *p_i;
+	llist_t *lo;
+
+	if((li = find_already(Ifound, include, MAKE_NEW)) == NULL)
+	    return;
+	Ifound = li;
+	if(include[0] != '/') {
+	    /* relative */
+	    int w;
+	    const char *p;
+
+	    p_i = strrchr(fname, '/');
+	    if(p_i == NULL) {
+		p = ".";
+		w = 1;
+	    } else {
+		w = (p_i-fname);
+		p = fname;
+	    }
+	    p_i = bb_asprint("%.*s/%s", w, p, include);
+	    if(store_include_fullpath(p_i, li))
+		return;
+	}
+	for(lo = Iop; lo; lo = lo->link) {
+	    p_i = bb_asprint("%s/%s", lo->data, include);
+	    if(store_include_fullpath(p_i, li))
+		return;
+	}
+	li->stored_path = NULL;
+	if(noiwarning)
+	    fprintf(stderr, "%s: Warning: #include \"%s\" not found in specified paths\n", fname, include);
+}
+
+static void parse_conf_opt(char *opt, const char *val, size_t recordsz)
+{
+	bb_key_t *cur = find_already(key_top, opt, MAKE_NEW);
+
+	if(cur != NULL) {
+	    /* new key, check old key if present after previous usage */
+	    char *s, *p;
+	    struct stat st;
+	    int fd;
+	    int cmp_ok = 0;
+	    static char *record_buf;
+	    static char *r_cmp;
+	    static size_t r_sz;
+
+	    recordsz += 2;  /* \n\0 */
+	    if(recordsz > r_sz) {
+		record_buf = xrealloc(record_buf, r_sz=recordsz);
+		r_cmp = xrealloc(r_cmp, recordsz);
+	    }
+	    s = record_buf;
+	    if(val)
+		sprintf(s, "#define %s%s%s\n", opt, (*val ? " " : ""), val);
+	    else
+		sprintf(s, "#undef %s\n", opt);
+	    /* may be short count " " */
+	    recordsz = strlen(s);
+	    /* key converting [A-Z] -> [a-z] */
+	    for(p = opt; *p; p++) {
+		if(*p >= 'A' && *p <= 'Z')
+			*p = *p - 'A' + 'a';
+		if(*p == '_')
+		    *p = '/';
+	    }
+	    p = bb_asprint("%s/%s.h", kp, opt);
+	    cur->stored_path = opt = p;
+	    while(*++p) {
+		/* Auto-create directories. */
+		if (*p == '/') {
+		    *p = '\0';
+		    if (stat(opt, &st) != 0 && mkdir(opt, 0755) != 0)
+			bb_error_d("mkdir(%s): %m", opt);
+		    *p = '/';
+		}
+	    }
+	    if(stat(opt, &st) == 0) {
+		    /* found */
+		    if(st.st_size == recordsz) {
+			fd = open(opt, O_RDONLY);
+			if(fd < 0 || read(fd, r_cmp, recordsz) != recordsz)
+			    bb_error_d("%s: %m", opt);
+			close(fd);
+			cmp_ok = memcmp(s, r_cmp, recordsz) == 0;
+		    }
+	    }
+	    if(!cmp_ok) {
+		fd = open(opt, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+		if(fd < 0 || write(fd, s, recordsz) != recordsz)
+		    bb_error_d("%s: %m", opt);
+		close(fd);
+	    }
+	    /* store only */
+	    cur->checked = 0;
+	    if(val) {
+		if(*val == '\0') {
+		    cur->value = "";
+		} else {
+		    cur->value = bb_xstrdup(val);
+		}
+	    } else {
+		cur->value = NULL;
+	    }
+	    key_top = cur;
+	} else {
+	    /* present already */
+	    for(cur = key_top; cur; cur = cur->next) {
+		if(strcmp(cur->keyname, opt) == 0) {
+		    cur->checked = 0;
+		    if(cur->value == NULL && val == NULL)
+			return;
+		    if((cur->value == NULL && val != NULL) ||
+			    (cur->value != NULL && val == NULL) ||
+			    strcmp(cur->value, val))
+			fprintf(stderr, "Warning: redefined %s\n", opt);
+		    return;
+		}
+	    }
+	}
+}
+
+static int show_dep(int first, bb_key_t *k, const char *a)
+{
+    bb_key_t *cur;
+
+    for(cur = k; cur; cur = cur->next) {
+	if(cur->checked && cur->stored_path) {
+	    if(first) {
+		const char *ext;
+
+		if(*a == '.' && a[1] == '/')
+		    a += 2;
+		ext = strrchr(a, '.');
+		if(ext && ext[1] == 'c' && ext[2] == '\0') {
+		    /* *.c -> *.o */
+		    printf("\n%.*s.o:", (ext - a), a);
+		} else {
+		    printf("\n%s:", a);
+		}
+		first = 0;
+	    } else {
+		printf(" \\\n  ");
+	    }
+	    printf(" %s", cur->stored_path);
+	}
+	cur->checked = 0;
+    }
+    return first;
+}
+
+static llist_t *files;
+
+static llist_t *filter_chd(const char *fe, const char *p, llist_t *pdirs)
+{
+    const char *e;
+    struct stat st;
+    char *fp;
+    char *afp;
+    llist_t *cfl;
+
+    if (*fe == '.')
+	return NULL;
+    fp = bb_asprint("%s/%s", p, fe);
+    if(stat(fp, &st)) {
+	fprintf(stderr, "Warning: stat(%s): %m", fp);
+	free(fp);
+	return NULL;
+    }
+    afp = bb_simplify_path(fp);
+    if(S_ISDIR(st.st_mode)) {
+	if(strcmp(kp, afp) == 0) {
+	    /* is autogenerated to kp/key* by previous usage */
+	    free(afp);
+	    free(fp);
+	    /* drop scan kp/ directory */
+	    return NULL;
+	}
+	free(afp);
+	return llist_add_to(pdirs, fp);
+    }
+    if(!S_ISREG(st.st_mode)) {
+	/* hmm, is device! */
+	free(afp);
+	free(fp);
+	return NULL;
+    }
+    e = strrchr(fe, '.');
+    if(e == NULL || !((e[1]=='c' || e[1]=='h') && e[2]=='\0')) {
+	/* direntry is not directory or *.[ch] */
+	free(afp);
+	free(fp);
+	return NULL;
+    }
+    for(cfl = configs; cfl; cfl = cfl->link) {
+	if(cfl->data && strcmp(cfl->data, afp) == 0) {
+	    /* parse configs.h */
+	    free(afp);
+	    c_lex(fp, 1);
+	    free(fp);
+	    free(cfl->data);
+	    cfl->data = NULL;
+	    return NULL;
+	}
+    }
+    free(fp);
+    /* direntry is *.[ch] regular file */
+    files = llist_add_to(files, afp);
+    return NULL;
+}
+
+static void scan_dir_find_ch_files(char *p)
+{
+    llist_t *dirs;
+    llist_t *d_add;
+    llist_t *d;
+    struct dirent *de;
+    DIR *dir;
+
+    dirs = llist_add_to(NULL, p);
+    /* emulate recursive */
+    while(dirs) {
+	d_add = NULL;
+	while(dirs) {
+	    dir = opendir(dirs->data);
+	    if (dir == NULL)
+		fprintf(stderr, "Warning: opendir(%s): %m", dirs->data);
+	    while ((de = readdir(dir)) != NULL) {
+		d = filter_chd(de->d_name, dirs->data, d_add);
+		if(d)
+		    d_add = d;
+	    }
+	    closedir(dir);
+	    if(dirs->data != p)
+		free(dirs->data);
+	    d = dirs;
+	    dirs = dirs->link;
+	    free(d);
+	}
+	dirs = d_add;
+    }
+}
+
+int main(int argc, char **argv)
+{
+	int generate_dep = 1;
+	char *s;
+	int i;
+	llist_t *fl;
+
+	while ((i = getopt(argc, argv, "I:c:dk:w")) > 0) {
+		switch(i) {
+		    case 'I':
+			    Iop = llist_add_to(Iop, optarg);
+			    break;
+		    case 'c':
+			    s = bb_simplify_path(optarg);
+			    configs = llist_add_to(configs, s);
+			    break;
+		    case 'd':
+			    generate_dep = 0;
+			    break;
+		    case 'k':
+			    if(kp)
+				bb_error_d("Hmm, why multiple -k?");
+			    kp = bb_simplify_path(optarg);
+			    break;
+		    case 'w':
+			    noiwarning = 1;
+			    break;
+		    default:
+			    show_usage();
+		}
+	}
+	if(argc > optind)
+	    show_usage();
+
+	/* defaults */
+	if(kp == NULL)
+	    kp = bb_simplify_path(INCLUDE_CONFIG_PATH);
+	if(Iop == NULL)
+	    Iop = llist_add_to(Iop, LOCAL_INCLUDE_PATH);
+	if(configs == NULL) {
+	    s = bb_simplify_path(INCLUDE_CONFIG_KEYS_PATH);
+	    configs = llist_add_to(configs, s);
+	}
+	scan_dir_find_ch_files(".");
+
+	for(fl = files; fl; fl = fl->link) {
+		c_lex(fl->data, 0);
+		if(generate_dep) {
+			i = show_dep(1, Ifound, fl->data);
+			i = show_dep(i, key_top, fl->data);
+			if(i == 0)
+				putchar('\n');
+		}
+	}
+	return 0;
+}
+
+void bb_error_d(const char *s, ...)
+{
+	va_list p;
+
+	va_start(p, s);
+	vfprintf(stderr, s, p);
+	va_end(p);
+	putc('\n', stderr);
+	exit(1);
+}
+
+
+void *xmalloc(size_t size)
+{
+	void *p = malloc(size);
+
+	if(p == NULL)
+		bb_error_d("memory exhausted");
+	return p;
+}
+
+void *xrealloc(void *p, size_t size) {
+	p = realloc(p, size);
+	if(p == NULL)
+		bb_error_d("memory exhausted");
+	return p;
+}
+
+char *bb_asprint(const char *format, ...)
+{
+	va_list p;
+	int r;
+	char *out;
+
+	va_start(p, format);
+	r = vasprintf(&out, format, p);
+	va_end(p);
+
+	if (r < 0)
+		bb_error_d("bb_asprint: %m");
+	return out;
+}
+
+llist_t *llist_add_to(llist_t *old_head, char *new_item)
+{
+	llist_t *new_head;
+
+	new_head = xmalloc(sizeof(llist_t));
+	new_head->data = new_item;
+	new_head->link = old_head;
+
+	return(new_head);
+}
+
+char *bb_xstrdup(const char *s)
+{
+    char *r = strdup(s);
+    if(r == NULL)
+	bb_error_d("memory exhausted");
+    return r;
+}
+
+char *bb_simplify_path(const char *path)
+{
+	char *s, *start, *p;
+
+	if (path[0] == '/')
+	      start = bb_xstrdup(path);
+	else {
+	      static char *pwd;
+
+	      if(pwd == NULL) {
+		    /* is not libbb, but this program have not chdir() */
+		    unsigned path_max = 512;
+		    char *cwd = xmalloc (path_max);
+#define PATH_INCR 32
+		    while (getcwd (cwd, path_max) == NULL) {
+			if(errno != ERANGE)
+			    bb_error_d("getcwd: %m");
+			path_max += PATH_INCR;
+			cwd = xrealloc (cwd, path_max);
+		    }
+		    pwd = cwd;
+	    }
+	    start = bb_asprint("%s/%s", pwd, path);
+	}
+	p = s = start;
+
+	do {
+		if (*p == '/') {
+			if (*s == '/') {        /* skip duplicate (or initial) slash */
+				continue;
+			} else if (*s == '.') {
+				if (s[1] == '/' || s[1] == 0) { /* remove extra '.' */
+					continue;
+				} else if ((s[1] == '.') && (s[2] == '/' || s[2] == 0)) {
+					++s;
+					if (p > start) {
+						while (*--p != '/');    /* omit previous dir */
+					}
+					continue;
+				}
+			}
+		}
+		*++p = *s;
+	} while (*++s);
+
+	if ((p == start) || (*p != '/')) {      /* not a trailing slash */
+		++p;                            /* so keep last character */
+	}
+	*p = 0;
+
+	return start;
+}