/* dirtools.c - reads and creates a list of directory entries Copyright (C) 1996-2000 Paul Sheer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include "stringtools.h" #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "coolwidget.h" #include "loadfile.h" #include "pool.h" #include "mad.h" char *dname (struct dirent *directentry) { int l; static char t[MAX_PATH_LEN]; l = NAMLEN (directentry); if (l >= MAX_PATH_LEN) l = MAX_PATH_LEN - 1; strncpy (t, directentry->d_name, l); t[l] = 0; return t; } /* Returns a \n separate list of directories or files in the directory *directory. The files are sorted alphabetacally. The list is malloc'ed and must be free'd. If f is FILELIST_DIRECTORIES_ONLY then only directories are returned. If f & FILELIST_ALL_FILES all files are returned. If f & FILELIST_FILES_ONLY, only files are returned. */ char *get_file_list (const char *directory, unsigned long f, char *filter) { struct dirent *directentry; struct stat stats; DIR *dir; char *list; int numentries = 0; long listsize; char path_fname[MAX_PATH_LEN]; POOL *p; p = pool_init (); if (filter) { if (!*filter) filter = "*"; } else filter = "*"; if ((dir = opendir (directory)) == NULL) /* No spaces here */ return (char *) strdup (_("Error: Cannot open directory.\n")); while ((directentry = readdir (dir))) { strcpy (path_fname, directory); strcat (path_fname, "/"); strcat (path_fname, dname (directentry)); if (!stat (path_fname, &stats) && strcmp (dname (directentry), ".")) { if (S_ISDIR (stats.st_mode)) { if (f & FILELIST_DIRECTORIES_ONLY) { if (regexp_match (filter, dname (directentry), match_file) == 1) { if (!pool_printf (p, "/%s\n", dname (directentry))) { closedir (dir); return 0; } numentries++; } } } else { if (f & FILELIST_FILES_ONLY) { if (regexp_match (filter, dname (directentry), match_file) == 1) { if (!pool_printf (p, "%s\n", dname (directentry))) { closedir (dir); return 0; } numentries++; } } } } } /* Now do a bubble sort on the list. (a directory list isn't long enough to warrant a quick sort) and the qsort command won't work on unevenly sized entries. */ pool_null (p); listsize = pool_length (p); list = (char *) pool_break (p); f = 1; if (numentries) { char *firststr, *secondstr; unsigned long i, r, q; while (f) { numentries--; r = 0; f = 0; for (i = 0; i < numentries; i++) { char *t; t = strchr (list + r, '\n'); if (t) { q = (unsigned long) t - (unsigned long) list + 1; if (strcmp (firststr = strline (list, r), secondstr = strline (list, q)) > 0) { strcpy (list + r, secondstr); r += strlen (secondstr); *(list + r++) = '\n'; memcpy (list + r, firststr, strlen (firststr)); f = 1; } else r = q; } else break; } } list[listsize - 1] = 0; /* remove the last \n */ } closedir (dir); return list; } int compare_fileentries (struct file_entry *file_entry1, struct file_entry *file_entry2) { #if 0 if (file_entry->options & FILELIST_SORT_...); #endif return (strcmp (file_entry1->name, file_entry2->name)); } struct file_entry *get_file_entry_list (const char *directory, unsigned long options, char *filter) { struct file_entry entry; struct file_entry *list; struct dirent *directentry; struct stat stats; DIR *dir; int numentries = 0; char path_fname[MAX_PATH_LEN]; POOL *p; p = pool_init (); if (filter) { if (!*filter) filter = "*"; } else filter = "*"; if ((dir = opendir (directory)) == NULL) { pool_free (p); return 0; } while ((directentry = readdir (dir))) { strcpy (path_fname, directory); strcat (path_fname, "/"); strcat (path_fname, dname (directentry)); if (!stat (path_fname, &stats) && strcmp (dname (directentry), ".")) { if (S_ISDIR (stats.st_mode)) { if (options & FILELIST_DIRECTORIES_ONLY) { if (regexp_match (filter, dname (directentry), match_file) == 1) { lstat (path_fname, &entry.stat); strcpy (entry.name, dname (directentry)); entry.options = options; if (!pool_write (p, (unsigned char *) &entry, sizeof (entry))) { pool_free (p); closedir (dir); return 0; } numentries++; } } } else { if (options & FILELIST_FILES_ONLY) { if (regexp_match (filter, dname (directentry), match_file) == 1) { lstat (path_fname, &entry.stat); strcpy (entry.name, dname (directentry)); entry.options = options; if (!pool_write (p, (unsigned char *) &entry, sizeof (entry))) { pool_free (p); closedir (dir); return 0; } numentries++; } } } } } memset (&entry, 0, sizeof (entry)); entry.options = FILELIST_LAST_ENTRY; if (!pool_write (p, (unsigned char *) &entry, sizeof (entry))) { pool_free (p); closedir (dir); return 0; } list = (struct file_entry *) pool_break (p); qsort((void *) list, numentries, sizeof (struct file_entry), (int (*) (const void *, const void *)) compare_fileentries); closedir (dir); return list; } static char *get_a_line (void *data, int line) { char **s; s = (char **) data; return s[line]; } /* generate a list of search results, and query the user if the list is longer that one: */ static char *do_user_file_list_search (Window parent, int x, int y, int lines, int columns, char *file_list, const char *base_name) { char *p = file_list, *ret = NULL; char **l = NULL; int list_len = 0, item, i; if (!file_list) return NULL; while ((p = strstr (p, base_name))) { char left_word_border, right_word_border; left_word_border = (p > file_list) ? *(p - 1) : '\n'; right_word_border = *(p + strlen (base_name)); if (left_word_border == '/' && (right_word_border == '\n' || right_word_border == '\0')) { char *eol, *bol, *r; eol = p + strlen (base_name); for (bol = p; bol > file_list && *(bol - 1) != '\n'; bol--); r = (char *) malloc ((int) (eol - bol + 1)); strncpy (r, bol, (int) (eol - bol)); r[(int) (eol - bol)] = '\0'; list_len++; l = (char **) realloc (l, sizeof (char *) * (list_len + 1)); l[list_len - 1] = r; l[list_len] = NULL; p = eol; if (!*p) break; } p++; if (!*p) break; } if (!list_len) return NULL; if (list_len == 1) item = 0; else item = CListboxDialog (parent, 20, 20, 60, list_len >= 15 ? 14 : list_len + 1, _("Multiple Files Found - Please Select"), 0, 0, list_len, get_a_line, (void *) l); /* free all list entries except the one we are returning: */ for (i = 0; i < list_len; i++) { if (i == item) ret = l[i]; else free (l[i]); } free (l); return ret; } /* generate a list of search results, and query the user if the list is longer that one: */ static char *do_user_file_list_complete (Window parent, int x, int y, int lines, int columns, char *file_list, const char *search_str) { POOL *pool; int c; char *p, *t, *r; pool = pool_init (); if (!file_list) return NULL; if (strlen (search_str) < 2) return NULL; /* list files starting with the text string first */ for (c = 0; c < 2; c++) { p = file_list; while ((p = strstr (p, search_str))) { char *eol, *bol; char left_word_border, right_word_border; left_word_border = (p > file_list) ? *(p - 1) : '\n'; right_word_border = *(p + strcspn (p, "/\n")); eol = p + strcspn (p, "\n"); for (bol = p; bol > file_list && *(bol - 1) != '\n'; bol--); if ((left_word_border == '\n' || (left_word_border == '/' && right_word_border != '/')) ^ c) { pool_write (pool, (unsigned char *) bol, (int) (eol - bol)); pool_write (pool, (unsigned char *) "\n", 1); } p = eol; if (!*p) break; p++; if (!*p) break; } } pool_null (pool); t = (char *) pool_break (pool); r = CTrivialSelectionDialog (parent, x, y, lines, columns, t, 0, 0); free (t); return r; } static char *_user_file_list_search (Window parent, int x, int y, int lines, int columns, const char *base_name, char *(*do_dialog) (Window, int, int, int, int, char *, const char *)) { static time_t last_stat_time = 0; static time_t last_change_time = 0; static char *whole_file = NULL; time_t t; struct stat st; if (!base_name) return NULL; time (&t); if (last_stat_time < t) { char *p; last_stat_time = t; p = (char *) malloc (strlen (home_dir) + strlen (FILELIST_FILE) + 2); strcpy (p, home_dir); strcat (p, FILELIST_FILE); if (stat (p, &st)) { CErrorDialog (0, 0, 0, _(" Open Personal File List "), get_sys_error (catstrs (_(" Error trying stat "), p, NULL))); free (p); if (whole_file) { free (whole_file); whole_file = NULL; } return NULL; } if (last_change_time && last_change_time == st.st_mtime) { free (p); return (*do_dialog) (parent, x, y, lines, columns, whole_file, base_name); } last_change_time = st.st_mtime; if (whole_file) free (whole_file); whole_file = loadfile (p, NULL); free (p); if (!whole_file) return NULL; } return (*do_dialog) (parent, x, y, lines, columns, whole_file, base_name); } char *user_file_list_search (Window parent, int x, int y, const char *base_name) { return _user_file_list_search (parent, x, y, 0, 0, base_name, do_user_file_list_search); } char *user_file_list_complete (Window parent, int x, int y, int lines, int columns, const char *search_str) { return _user_file_list_search (parent, x, y, lines, columns, search_str, do_user_file_list_complete); }