#include #include #include #include #include #include "conf.h" #include "dir.h" #include "err.h" #include "mem.h" /* The reason for this routine is that xterm/ curses fucks * up on some configurations if feed with non ascii characters. */ static void ascii_memcpy (char *to, char *from, int n) { int c; for (;n>0;n--) { c = *from++; if (c < 32 || c > 126) c = '?'; *to++ = c; } } /* Returns ERROR for no match */ static int match_format (struct CONFIG *cfg, char *s) { int l, ml, i, j; char *s2, *m; l = strlen (s); for (i=0; iformats; i++) { m = cfg->format[i].strings; ml = cfg->format[i].match_len; if (l >= ml) { s2 = s + l - ml; for (j=0; jformats) i = ERROR; return i; } static int is_dir (char *path) { struct stat buf; if (!stat (path, &buf)) { if (buf.st_mode &= S_IFDIR) return 1; } return 0; } static int is_file (char *path) { struct stat buf; if (!stat (path, &buf)) { if (buf.st_mode &= S_IFREG) return 1; } return 0; } /* Return 1 if string b is less than string a. * a and b are not null terminated so I can't use strcmp. */ static int swap (char *a, char *b, int as, int bs) { int i, f = 0, n; if (as < bs) n = as; else n = bs; for (i=0;i b[i]) { f = 1; break; } if (a[i] < b[i]) break; } if (f) return 1; if (i == n) { if (as > bs) return 1; } return 0; } /* Sort directory by type and alphabeticaly. */ static void sort (struct DIR_INFO *dinfo, int parent) { char *a, *b; int *list_old = 0, *list_new = 0, items, i, j, k, f, t, as, bs; struct DIR_ITEM *dir_items = 0; items = dir_count_parent (dinfo, parent); if (!items) return; mem_resize ((void **)&list_old, sizeof(int) * items); mem_resize ((void **)&list_new, sizeof(int) * items); mem_resize ((void **)&dir_items, sizeof(struct DIR_ITEM) * items); i = 0; j = 0; while ((i = dir_match_parent (dinfo, parent, i)) != -1) { list_old[j] = i; list_new[j++] = i++; } do { f=0; for (i=0; iitem[j].type > dinfo->item[k].type) { t = list_new[i]; list_new[i] = list_new[i+1]; list_new[i+1] = t; f = 1; } } } while (f); do { f = 0; for (i=0; iitem[j].type == dinfo->item[k].type) { a = dinfo->strings + dinfo->item[j].offset; b = dinfo->strings + dinfo->item[k].offset; as = dinfo->item[j].size; bs = dinfo->item[k].size; if (swap (a, b, as, bs)) { t = list_new[i]; list_new[i] = list_new[i+1]; list_new[i+1] = t; f = 1; } } } } while (f); for (i=0; iitem[list_new[i]]; for (i=0; iitem[list_old[i]] = dir_items[i]; free (list_old); free (list_new); free (dir_items); } /* Returns position of added entry. */ static int add_item (struct DIR_INFO *dinfo, char *name, int type, int tree, int level, int parent) { int num, size, old_size; struct DIR_ITEM *ditem; num = dinfo->items; size = sizeof(struct DIR_ITEM) * (num + 1); mem_resize ((void *)&dinfo->item, size); ditem = &dinfo->item[num]; dinfo->items = num + 1; size = strlen (name); old_size = dinfo->size; dinfo->size += size; mem_resize ((void *)&dinfo->strings, dinfo->size); ascii_memcpy (dinfo->strings + old_size, name, size); ditem->queued = 0; ditem->type = type; ditem->tree = tree; ditem->level = level; ditem->parent = parent; ditem->offset = old_size; ditem->size = size; return num; } /* Return -1 for error */ static int add_items (struct DIR_INFO *dinfo, struct CONFIG *cfg, char *path, int tree, int level, int parent) { int format; DIR *dir; struct dirent *d; if (!(dir = opendir (path))) return ERROR; if (chdir (path)) return ERROR; while ((d = readdir (dir))) { if (is_dir (d->d_name) & (d->d_name[0] != '.')) { add_item (dinfo, d->d_name, 0, tree, level, parent); } else { if (is_file (d->d_name)) { format = match_format (cfg, d->d_name); if (format != ERROR) { add_item (dinfo, d->d_name, format + 1, tree, level, parent); dinfo->files++; } } } } closedir (dir); sort (dinfo, parent); return E_OK; } /* Return position or -1 for not found */ static int find_dir (struct DIR_INFO *dinfo, int tree, int level, int start_dir) { int i; for (i=start_dir; iitems; i++) { if (dinfo->item[i].tree == tree) { if (dinfo->item[i].level == level) { if (! dinfo->item[i].type) return i; } } } return ERROR; } /* Builds path to item, returns malloced memory */ char *dir_build_path (struct DIR_INFO *dinfo, int item) { char *str = 0, *str2, *str3; int db_n = 0, str_s = 0, i, j; struct DIR_BUILD *db = 0; struct DIR_ITEM *di = &dinfo->item[item]; db_n = di->level + 1; mem_resize ((void *)&db, db_n * sizeof(struct DIR_BUILD)); for (i=0;istrings + di->offset; db[i].size = di->size; str_s += di->size + 1; di = &dinfo->item[di->parent]; } str_s++; mem_resize ((void *)&str, str_s); str2 = str; for (i=db_n-1;i>=0;i--) { str3 = db[i].name; for (j=db[i].size;j>0;j--) *str2++ = *str3++; if (i) { *str2++ = '/'; } } *str2 = 0; free (db); return str; } /* Returns -1 if path can't be opened */ int dir_recurse (struct DIR_INFO *dinfo, struct CONFIG *cfg, char *path, int tree) { int level = 0, level_hits = 0, epos; char *s, opath[1025]; DIR *dir; dir = opendir (path); if (!dir) return ERROR; epos = add_item (dinfo, path, 0, tree, level, -1); closedir (dir); if (! getcwd (opath, 1025)) /* should use PATH_MAX */ return ERROR; do { if (epos == ERROR) { level++; level_hits = 0; epos = 0; } epos = find_dir (dinfo, tree, level, epos); if (epos != ERROR) { s = dir_build_path (dinfo, epos); add_items (dinfo, cfg, s, tree, level + 1, epos); free (s); chdir (opath); level_hits++; epos++; } } while (level_hits); return E_OK; } /* Return number of items matched by parent */ int dir_count_parent (struct DIR_INFO *dinfo, int parent) { int i, num = 0; for (i=0; iitems; i++) { if (dinfo->item[i].parent == parent) { num++; } } return num; } /* Return item matched by parent */ int dir_match_parent (struct DIR_INFO *dinfo, int parent, int start_dir) { int i; for (i=start_dir; iitems; i++) { if (dinfo->item[i].parent == parent) { return i; } } return ERROR; } /* Return item matched by level == 0 */ int dir_match_null_level (struct DIR_INFO *dinfo, int start_dir) { int i; for (i=start_dir; iitems; i++) { if (!dinfo->item[i].level) { return i; } } return ERROR; }