ls.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /**
  2. \file lib/gis/ls.c
  3. \brief Functions to list the files in a directory.
  4. \author Paul Kelly
  5. (C) 2007, 2008 by the GRASS Development Team
  6. This program is free software under the GNU General Public
  7. License (>=v2). Read the file COPYING that comes with GRASS
  8. for details.
  9. */
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <sys/types.h>
  14. #include <dirent.h>
  15. #include <unistd.h>
  16. #include <grass/gis.h>
  17. #include <grass/config.h>
  18. #include <grass/glocale.h>
  19. #ifdef HAVE_TERMIOS_H
  20. # include <termios.h>
  21. #endif
  22. #ifdef HAVE_SYS_IOCTL_H
  23. # include <sys/ioctl.h>
  24. #endif
  25. typedef int ls_filter_func(const char * /*filename */ , void * /*closure */ );
  26. static struct state {
  27. ls_filter_func *ls_filter;
  28. void *ls_closure;
  29. ls_filter_func *ls_ex_filter;
  30. void *ls_ex_closure;
  31. } state;
  32. static struct state *st = &state;
  33. static int cmp_names(const void *aa, const void *bb)
  34. {
  35. char *const *a = (char *const *)aa;
  36. char *const *b = (char *const *)bb;
  37. return strcmp(*a, *b);
  38. }
  39. /**
  40. * \brief Sets a function and its complementary data for G_ls2 filtering.
  41. *
  42. * Defines a filter function and its rule data that allow G_ls2 to filter out
  43. * unwanted file names. Call this function before G_ls2.
  44. *
  45. * \param func Filter callback function to compare a file name and closure
  46. * pattern (if NULL, no filter will be used).
  47. * func(filename, closure) should return 1 on success, 0 on
  48. * failure.
  49. * \param closure Data used to determine if a file name matches the rule.
  50. **/
  51. void G_set_ls_filter(ls_filter_func *func, void *closure)
  52. {
  53. st->ls_filter = func;
  54. st->ls_closure = closure;
  55. }
  56. void G_set_ls_exclude_filter(ls_filter_func *func, void *closure)
  57. {
  58. st->ls_ex_filter = func;
  59. st->ls_ex_closure = closure;
  60. }
  61. /**
  62. * \brief Stores a sorted directory listing in an array
  63. *
  64. * The filenames in the specified directory are stored in an array of
  65. * strings, then sorted alphabetically. Each filename has space allocated
  66. * using G_store(), which can be freed using G_free() if necessary. The
  67. * same goes for the array itself.
  68. *
  69. *
  70. * \param dir Directory to list
  71. * \param num_files Pointer to an integer in which the total number of
  72. * files listed will be stored
  73. *
  74. * \return Pointer to array of strings containing the listing
  75. **/
  76. char **G_ls2(const char *dir, int *num_files)
  77. {
  78. struct dirent *dp;
  79. DIR *dfd;
  80. char **dir_listing = NULL;
  81. int n = 0;
  82. if ((dfd = opendir(dir)) == NULL)
  83. G_fatal_error(_("Unable to open directory %s"), dir);
  84. while ((dp = readdir(dfd)) != NULL) {
  85. if (dp->d_name[0] == '.') /* Don't list hidden files */
  86. continue;
  87. if (st->ls_filter && !(*st->ls_filter)(dp->d_name, st->ls_closure))
  88. continue;
  89. if (st->ls_ex_filter && (*st->ls_ex_filter)(dp->d_name, st->ls_ex_closure))
  90. continue;
  91. dir_listing = (char **)G_realloc(dir_listing, (1 + n) * sizeof(char *));
  92. dir_listing[n] = G_store(dp->d_name);
  93. n++;
  94. }
  95. closedir(dfd);
  96. /* Sort list of filenames alphabetically */
  97. qsort(dir_listing, n, sizeof(char *), cmp_names);
  98. *num_files = n;
  99. return dir_listing;
  100. }
  101. /**
  102. * \brief Prints a directory listing to a stream, in prettified column format
  103. *
  104. * A replacement for system("ls -C"). Lists the contents of the directory
  105. * specified to the given stream, e.g. stderr. Tries to determine an
  106. * appropriate column width to keep the number of lines used to a minimum
  107. * and look pretty on the screen.
  108. *
  109. * \param dir Directory to list
  110. * \param stream Stream to print listing to
  111. **/
  112. void G_ls(const char *dir, FILE * stream)
  113. {
  114. int i, n;
  115. char **dir_listing = G_ls2(dir, &n);
  116. G_ls_format(dir_listing, n, 0, stream);
  117. for (i = 0; i < n; i++)
  118. G_free(dir_listing[i]);
  119. G_free(dir_listing);
  120. }
  121. /**
  122. * \brief Prints a listing of items to a stream, in prettified column format
  123. *
  124. * Lists the contents of the array passed to the given stream, e.g. stderr.
  125. * Prints the number of items specified by "perline" to each line, unless
  126. * perline is given as 0 in which case the function tries to determine an
  127. * appropriate column width to keep the number of lines used to a minimum
  128. * and look pretty on the screen.
  129. *
  130. * \param list Array of strings containing items to be printed
  131. * \param num_items Number of items in the array
  132. * \param perline Number of items to print per line, 0 for autodetect
  133. * \param stream Stream to print listing to
  134. **/
  135. void G_ls_format(char **list, int num_items, int perline, FILE * stream)
  136. {
  137. int i;
  138. int field_width, column_height;
  139. int screen_width = 80; /* Default width of 80 columns */
  140. if (num_items < 1)
  141. return; /* Nothing to print */
  142. #ifdef TIOCGWINSZ
  143. /* Determine screen_width if possible */
  144. {
  145. struct winsize size;
  146. if (ioctl(fileno(stream), TIOCGWINSZ, (char *)&size) == 0)
  147. screen_width = size.ws_col;
  148. }
  149. #endif
  150. if (perline == 0) {
  151. int max_len = 0;
  152. for (i = 0; i < num_items; i++) {
  153. /* Find maximum filename length */
  154. if (strlen(list[i]) > max_len)
  155. max_len = strlen(list[i]);
  156. }
  157. /* Auto-fit the number of items that will
  158. * fit per line (+1 because of space after item) */
  159. perline = screen_width / (max_len + 1);
  160. if (perline < 1)
  161. perline = 1;
  162. }
  163. /* Field width to accommodate longest filename */
  164. field_width = screen_width / perline;
  165. /* Longest column height (i.e. num_items <= perline * column_height) */
  166. column_height = (num_items / perline) + ((num_items % perline) > 0);
  167. {
  168. const int max
  169. = num_items + column_height - (num_items % column_height);
  170. char **next;
  171. for (i = 1, next = list; i <= num_items; i++) {
  172. char **cur = next;
  173. next += column_height;
  174. if (next >= list + num_items) {
  175. /* the next item has to be on the other line */
  176. next -= (max - 1 - (next < list + max ? column_height : 0));
  177. fprintf(stream, "%s\n", *cur);
  178. }
  179. else {
  180. fprintf(stream, "%-*s", field_width, *cur);
  181. }
  182. }
  183. }
  184. }