/** \file lib/gis/ls.c \brief Functions to list the files in a directory. \author Paul Kelly (C) 2007, 2008 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. */ #include #include #include #include #include #include #include #include #include #ifdef HAVE_TERMIOS_H # include #endif #ifdef HAVE_SYS_IOCTL_H # include #endif typedef int ls_filter_func(const char * /*filename */ , void * /*closure */ ); static struct state { ls_filter_func *ls_filter; void *ls_closure; ls_filter_func *ls_ex_filter; void *ls_ex_closure; } state; static struct state *st = &state; static int cmp_names(const void *aa, const void *bb) { char *const *a = (char *const *)aa; char *const *b = (char *const *)bb; return strcmp(*a, *b); } /** * \brief Sets a function and its complementary data for G_ls2 filtering. * * Defines a filter function and its rule data that allow G_ls2 to filter out * unwanted file names. Call this function before G_ls2. * * \param func Filter callback function to compare a file name and closure * pattern (if NULL, no filter will be used). * func(filename, closure) should return 1 on success, 0 on * failure. * \param closure Data used to determine if a file name matches the rule. **/ void G_set_ls_filter(ls_filter_func *func, void *closure) { st->ls_filter = func; st->ls_closure = closure; } void G_set_ls_exclude_filter(ls_filter_func *func, void *closure) { st->ls_ex_filter = func; st->ls_ex_closure = closure; } /** * \brief Stores a sorted directory listing in an array * * The filenames in the specified directory are stored in an array of * strings, then sorted alphabetically. Each filename has space allocated * using G_store(), which can be freed using G_free() if necessary. The * same goes for the array itself. * * * \param dir Directory to list * \param num_files Pointer to an integer in which the total number of * files listed will be stored * * \return Pointer to array of strings containing the listing **/ char **G_ls2(const char *dir, int *num_files) { struct dirent *dp; DIR *dfd; char **dir_listing = NULL; int n = 0; if ((dfd = opendir(dir)) == NULL) G_fatal_error(_("Unable to open directory %s"), dir); while ((dp = readdir(dfd)) != NULL) { if (dp->d_name[0] == '.') /* Don't list hidden files */ continue; if (st->ls_filter && !(*st->ls_filter)(dp->d_name, st->ls_closure)) continue; if (st->ls_ex_filter && (*st->ls_ex_filter)(dp->d_name, st->ls_ex_closure)) continue; dir_listing = (char **)G_realloc(dir_listing, (1 + n) * sizeof(char *)); dir_listing[n] = G_store(dp->d_name); n++; } closedir(dfd); /* Sort list of filenames alphabetically */ qsort(dir_listing, n, sizeof(char *), cmp_names); *num_files = n; return dir_listing; } /** * \brief Prints a directory listing to a stream, in prettified column format * * A replacement for system("ls -C"). Lists the contents of the directory * specified to the given stream, e.g. stderr. Tries to determine an * appropriate column width to keep the number of lines used to a minimum * and look pretty on the screen. * * \param dir Directory to list * \param stream Stream to print listing to **/ void G_ls(const char *dir, FILE * stream) { int i, n; char **dir_listing = G_ls2(dir, &n); G_ls_format(dir_listing, n, 0, stream); for (i = 0; i < n; i++) G_free(dir_listing[i]); G_free(dir_listing); } /** * \brief Prints a listing of items to a stream, in prettified column format * * Lists the contents of the array passed to the given stream, e.g. stderr. * Prints the number of items specified by "perline" to each line, unless * perline is given as 0 in which case the function tries to determine an * appropriate column width to keep the number of lines used to a minimum * and look pretty on the screen. * * \param list Array of strings containing items to be printed * \param num_items Number of items in the array * \param perline Number of items to print per line, 0 for autodetect * \param stream Stream to print listing to **/ void G_ls_format(char **list, int num_items, int perline, FILE * stream) { int i; int field_width, column_height; int screen_width = 80; /* Default width of 80 columns */ if (num_items < 1) return; /* Nothing to print */ #ifdef TIOCGWINSZ /* Determine screen_width if possible */ { struct winsize size; if (ioctl(fileno(stream), TIOCGWINSZ, (char *)&size) == 0) screen_width = size.ws_col; } #endif if (perline == 0) { int max_len = 0; for (i = 0; i < num_items; i++) { /* Find maximum filename length */ if (strlen(list[i]) > max_len) max_len = strlen(list[i]); } /* Auto-fit the number of items that will * fit per line (+1 because of space after item) */ perline = screen_width / (max_len + 1); if (perline < 1) perline = 1; } /* Field width to accommodate longest filename */ field_width = screen_width / perline; /* Longest column height (i.e. num_items <= perline * column_height) */ column_height = (num_items / perline) + ((num_items % perline) > 0); { const int max = num_items + column_height - (num_items % column_height); char **next; for (i = 1, next = list; i <= num_items; i++) { char **cur = next; next += column_height; if (next >= list + num_items) { /* the next item has to be on the other line */ next -= (max - 1 - (next < list + max ? column_height : 0)); fprintf(stream, "%s\n", *cur); } else { fprintf(stream, "%-*s", field_width, *cur); } } } }