home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / mc454src.zip / mc-4.5.4.src / mc-4.5.4 / src / dir.c < prev    next >
C/C++ Source or Header  |  1999-01-04  |  17KB  |  651 lines

  1. /* Directory routines
  2.    Copyright (C) 1994 Miguel de Icaza.
  3.    
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2 of the License, or
  7.    (at your option) any later version.
  8.    
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. #include <config.h>
  19. #define DIR_H_INCLUDE_HANDLE_DIRENT
  20. #include "tty.h"
  21. #include "fs.h"
  22. #include <string.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <errno.h>
  26. #include <sys/stat.h>
  27. #include "x.h"
  28. #include "mad.h"
  29. #include "global.h"
  30. #include "dir.h"
  31. #include "util.h"
  32. #include "dialog.h"
  33. #include "tree.h"
  34. #include "../vfs/vfs.h"
  35.  
  36. /* "$Id: dir.c,v 1.9 1998/12/24 21:37:38 norbert Exp $" */
  37.  
  38. /* If true show files starting with a dot */
  39. int show_dot_files = 1;
  40.  
  41. /* If true show files ending in ~ */
  42. int show_backups = 0;
  43.  
  44. /* If false then directories are shown separately from files */
  45. int mix_all_files = 0;
  46.  
  47. /* Reverse flag */
  48. static int reverse = 1;
  49.  
  50. /* Are the files sorted case sensitively? */
  51. static int case_sensitive = OS_SORT_CASE_SENSITIVE_DEFAULT;
  52.  
  53. #define MY_ISDIR(x) ( (S_ISDIR (x->buf.st_mode) || x->f.link_to_dir) ? 1 : 0)
  54.  
  55. sort_orders_t sort_orders [SORT_TYPES_TOTAL] = {
  56.     { N_("&Unsorted"),    unsorted },
  57.     { N_("&Name"),        sort_name },
  58.     { N_("&Extension"),   sort_ext },
  59.     { N_("&Modify time"), sort_time },
  60.     { N_("&Access time"), sort_atime },
  61.     { N_("&Change time"), sort_ctime },
  62.     { N_("&Size"),        sort_size },
  63.     { N_("&Inode"),       sort_inode },
  64.  
  65.     /* New sort orders */
  66.     { N_("&Type"),        sort_type },
  67.     { N_("&Links"),       sort_links },
  68.     { N_("N&GID"),        sort_ngid },
  69.     { N_("N&UID"),        sort_nuid },
  70.     { N_("&Owner"),       sort_owner },
  71.     { N_("&Group"),       sort_group }
  72. };
  73.  
  74. #define string_sortcomp(a,b) (case_sensitive ? strcmp (a,b) : strcasecmp (a,b))
  75.  
  76. int
  77. unsorted (const file_entry *a, const file_entry *b)
  78. {
  79.     return 0;
  80. }
  81.  
  82. int
  83. sort_name (const file_entry *a, const file_entry *b)
  84. {
  85.     int ad = MY_ISDIR (a);
  86.     int bd = MY_ISDIR (b);
  87.     
  88.     if (ad == bd || mix_all_files)
  89.     return string_sortcomp (a->fname, b->fname) * reverse;
  90.     return bd-ad;
  91. }
  92.  
  93. int
  94. sort_ext (const file_entry *a, const file_entry *b)
  95. {
  96.     char *exta, *extb;
  97.     int r;
  98.     int ad = MY_ISDIR (a);
  99.     int bd = MY_ISDIR (b);
  100.  
  101.     if (ad == bd || mix_all_files){
  102.     exta = extension (a->fname);
  103.     extb = extension (b->fname);
  104.     r = string_sortcomp (exta, extb);
  105.     if (r)
  106.         return r * reverse;
  107.     else
  108.         return sort_name (a, b);
  109.     } else
  110.     return bd-ad;
  111. }
  112.  
  113. int
  114. sort_owner (const file_entry *a, const file_entry *b)
  115. {
  116.     int ad = MY_ISDIR (a);
  117.     int bd = MY_ISDIR (b);
  118.     
  119.     if (ad == bd || mix_all_files)
  120.     return string_sortcomp (get_owner (a->buf.st_uid), get_owner (a->buf.st_uid)) * reverse;
  121.     return bd-ad;
  122. }
  123.  
  124. int
  125. sort_group (const file_entry *a, const file_entry *b)
  126. {
  127.     int ad = MY_ISDIR (a);
  128.     int bd = MY_ISDIR (b);
  129.     
  130.     if (ad == bd || mix_all_files)
  131.     return string_sortcomp (get_group (a->buf.st_gid), get_group (a->buf.st_gid)) * reverse;
  132.     return bd-ad;
  133. }
  134.  
  135. int
  136. sort_time (const file_entry *a, const file_entry *b)
  137. {
  138.     int ad = MY_ISDIR (a);
  139.     int bd = MY_ISDIR (b);
  140.  
  141.     if (ad == bd || mix_all_files)
  142.     return (a->buf.st_mtime - b->buf.st_mtime) * reverse;
  143.     else
  144.     return bd-ad;
  145. }
  146.  
  147. int
  148. sort_ctime (const file_entry *a, const file_entry *b)
  149. {
  150.     int ad = MY_ISDIR (a);
  151.     int bd = MY_ISDIR (b);
  152.  
  153.     if (ad == bd || mix_all_files)
  154.     return (a->buf.st_ctime - b->buf.st_ctime) * reverse;
  155.     else
  156.     return bd-ad;
  157. }
  158.  
  159. int
  160. sort_atime (const file_entry *a, const file_entry *b)
  161. {
  162.     int ad = MY_ISDIR (a);
  163.     int bd = MY_ISDIR (b);
  164.  
  165.     if (ad == bd || mix_all_files)
  166.     return (a->buf.st_atime - b->buf.st_atime) * reverse;
  167.     else
  168.     return bd-ad;
  169. }
  170.  
  171. int
  172. sort_inode (const file_entry *a, const file_entry *b)
  173. {
  174.     int ad = MY_ISDIR (a);
  175.     int bd = MY_ISDIR (b);
  176.  
  177.     if (ad == bd || mix_all_files)
  178.     return (a->buf.st_ino - b->buf.st_ino) * reverse;
  179.     else
  180.     return bd-ad;
  181. }
  182.  
  183. int
  184. sort_size (const file_entry *a, const file_entry *b)
  185. {
  186.     int ad = MY_ISDIR (a);
  187.     int bd = MY_ISDIR (b);
  188.  
  189.     if (ad == bd || mix_all_files)
  190.     return (b->buf.st_size - a->buf.st_size) * reverse;
  191.     else
  192.     return bd-ad;
  193. }
  194.  
  195. int
  196. sort_links (const file_entry *a, const file_entry *b)
  197. {
  198.     int ad = MY_ISDIR (a);
  199.     int bd = MY_ISDIR (b);
  200.  
  201.     if (ad == bd || mix_all_files)
  202.     return (b->buf.st_nlink - a->buf.st_nlink) * reverse;
  203.     else
  204.     return bd-ad;
  205. }
  206.  
  207. int
  208. sort_ngid (const file_entry *a, const file_entry *b)
  209. {
  210.     int ad = MY_ISDIR (a);
  211.     int bd = MY_ISDIR (b);
  212.  
  213.     if (ad == bd || mix_all_files)
  214.     return (b->buf.st_gid - a->buf.st_gid) * reverse;
  215.     else
  216.     return bd-ad;
  217. }
  218.  
  219. int
  220. sort_nuid (const file_entry *a, const file_entry *b)
  221. {
  222.     int ad = MY_ISDIR (a);
  223.     int bd = MY_ISDIR (b);
  224.  
  225.     if (ad == bd || mix_all_files)
  226.     return (b->buf.st_uid - a->buf.st_uid) * reverse;
  227.     else
  228.     return bd-ad;
  229. }
  230.  
  231. inline static int
  232. file_type_to_num (const file_entry *fe)
  233. {
  234.     const struct stat *s = &fe->buf;
  235.     
  236.     if (S_ISDIR (s->st_mode))
  237.     return 0;
  238.     if (S_ISLNK (s->st_mode)){
  239.     if (fe->f.link_to_dir)
  240.         return 1;
  241.     if (fe->f.stalled_link)
  242.         return 2;
  243.     else
  244.         return 3;
  245.     }
  246.     if (S_ISSOCK (s->st_mode))
  247.     return 4;
  248.     if (S_ISCHR (s->st_mode))
  249.     return 5;
  250.     if (S_ISBLK (s->st_mode))
  251.     return 6;
  252.     if (S_ISFIFO (s->st_mode))
  253.     return 7;
  254.     if (is_exe (s->st_mode))
  255.     return 8;
  256.     return 9;
  257. }
  258.  
  259. int
  260. sort_type (const file_entry *a, const file_entry *b)
  261. {
  262.     int aa  = file_type_to_num (a);
  263.     int bb  = file_type_to_num (b);
  264.     
  265.     return bb-aa;
  266. }
  267.  
  268.  
  269. void
  270. do_sort (dir_list *list, sortfn *sort, int top, int reverse_f, int case_sensitive_f)
  271. {
  272.     int i;
  273.     file_entry tmp_fe;
  274.  
  275.     for (i = 0; i < top + 1; i++) {             /* put ".." first in list */
  276.     if (!strcmp (list->list [i].fname, "..")) {
  277.             if (i > 0) {                        /* swap [i] and [0] */
  278.                 memcpy (&tmp_fe, &(list->list [0]), sizeof (file_entry));
  279.                 memcpy (&(list->list [0]), &(list->list [i]), sizeof (file_entry));
  280.                 memcpy (&(list->list [i]), &tmp_fe, sizeof (file_entry));
  281.             }
  282.             break;
  283.         }
  284.     }
  285.  
  286.     reverse = reverse_f ? -1 : 1;
  287.     case_sensitive = case_sensitive_f;
  288.     qsort (&(list->list) [1], top, sizeof (file_entry), sort);
  289. }
  290.  
  291. void clean_dir (dir_list *list, int count)
  292. {
  293.     int i;
  294.  
  295.     for (i = 0; i < count; i++){
  296.     free (list->list [i].fname);
  297.     list->list [i].fname = 0;
  298.     }
  299. }
  300.  
  301. static int 
  302. add_dotdot_to_list (dir_list *list, int index)
  303. {
  304.     char buffer [MC_MAXPATHLEN + MC_MAXPATHLEN];
  305.     char *p;
  306.     int i = 0;
  307.     
  308.     /* Need to grow the *list? */
  309.     if (index == list->size) {
  310.     list->list = realloc (list->list, sizeof (file_entry) *
  311.                   (list->size + RESIZE_STEPS));
  312.     if (!list->list)
  313.         return 0;
  314.     list->size += RESIZE_STEPS;
  315.     }
  316.  
  317.     (list->list) [index].fnamelen = 2;
  318.     (list->list) [index].fname = strdup ("..");
  319.     (list->list) [index].f.link_to_dir = 0;
  320.     (list->list) [index].f.stalled_link = 0;
  321.     (list->list) [index].f.dir_size_computed = 0;
  322.     
  323.     /* FIXME: We need to get the panel definition! to use file_mark */
  324.     (list->list) [index].f.marked = 0;
  325.     mc_get_current_wd (buffer, sizeof (buffer) - 1 );
  326.     if (buffer [strlen (buffer) - 1] == PATH_SEP)
  327.         buffer [strlen (buffer) - 1] = 0;
  328.     for (;;) {
  329.         strcat (buffer, PATH_SEP_STR "..");
  330.         p = vfs_canon (buffer);
  331.         if (mc_stat (p, &((list->list) [index].buf)) != -1){
  332.         free (p);
  333.             break;
  334.     }
  335.         i = 1;
  336.         if (!strcmp (p, PATH_SEP_STR)){
  337.         free (p);
  338.             return 1;
  339.     }
  340.     strcpy (buffer, p);
  341.     free (p);
  342.     }
  343.  
  344. /* Commented out to preserve a usable '..'. What's the purpose of this
  345.  * three lines? (Norbert) */
  346. #if 0
  347.     if (i) { /* So there is bogus information on the .. directory's stat */
  348.         (list->list) [index].buf.st_mode &= ~0444;
  349.     }
  350. #endif
  351.     return 1;
  352. }
  353.  
  354. /* Used to set up a directory list when there is no access to a directory */
  355. int set_zero_dir (dir_list *list)
  356. {
  357.     return (add_dotdot_to_list (list, 0));
  358. }
  359.  
  360. /* If you change handle_dirent then check also handle_path. */
  361. /* Return values: -1 = failure, 0 = don't add, 1 = add to the list */
  362. int handle_dirent (dir_list *list, char *filter, struct dirent *dp,
  363.            struct stat *buf1, int next_free, int *link_to_dir,
  364.            int *stalled_link)
  365. {
  366.     if (dp->d_name [0] == '.' && dp->d_name [1] == 0)
  367.     return 0;
  368.     if (!show_dot_files){
  369.     if (dp->d_name [0] == '.'){
  370.         if (!(dp->d_name [1] == 0))
  371.         if (!(dp->d_name [1] == '.' && NLENGTH (dp) == 2))
  372.             return 0;
  373.     }
  374.     }
  375.     if (!show_backups && dp->d_name [NLENGTH (dp)-1] == '~')
  376.     return 0;
  377.     if (mc_lstat (dp->d_name, buf1) == -1) {
  378.     message(1, _(" Error "), _("File exists but can not be stat-ed: %s %s"), dp->d_name, strerror(errno));
  379.         return 0;
  380.     }
  381.  
  382.     if (S_ISDIR (buf1->st_mode))
  383.     tree_check (dp->d_name);
  384.     
  385.     /* A link to a file or a directory? */
  386.     *link_to_dir = 0;
  387.     *stalled_link = 0;
  388.     if (S_ISLNK(buf1->st_mode)){
  389.     struct stat buf2;
  390.     if (!mc_stat (dp->d_name, &buf2))
  391.         *link_to_dir = S_ISDIR(buf2.st_mode) != 0;
  392.     else
  393.         *stalled_link = 1;
  394.     }
  395.     if (!(S_ISDIR(buf1->st_mode) || *link_to_dir) && filter &&
  396.     !regexp_match (filter, dp->d_name, match_file))
  397.     return 0;
  398.  
  399.     /* Need to grow the *list? */
  400.     if (next_free == list->size){
  401.     list->list = realloc (list->list, sizeof (file_entry) *
  402.                   (list->size + RESIZE_STEPS));
  403.     if (!list->list)
  404.         return -1;
  405.     list->size += RESIZE_STEPS;
  406.     }
  407.     return 1;
  408. }
  409.  
  410. /* handle_path is a simplified handle_dirent. The difference is that 
  411.    handle_path doesn't pay attention to show_dot_files and show_backups.
  412.    Moreover handle_path can't be used with a filemask. 
  413.    If you change handle_path then check also handle_dirent. */
  414. /* Return values: -1 = failure, 0 = don't add, 1 = add to the list */
  415. int handle_path (dir_list *list, char *path,
  416.            struct stat *buf1, int next_free, int *link_to_dir,
  417.            int *stalled_link)
  418. {
  419.     if (path [0] == '.' && path [1] == 0)
  420.     return 0;
  421.     if (mc_lstat (path, buf1) == -1)
  422.         return 0;
  423.  
  424.     if (S_ISDIR (buf1->st_mode))
  425.     tree_check (path);
  426.     
  427.     /* A link to a file or a directory? */
  428.     *link_to_dir = 0;
  429.     *stalled_link = 0;
  430.     if (S_ISLNK(buf1->st_mode)){
  431.     struct stat buf2;
  432.     if (!mc_stat (path, &buf2))
  433.         *link_to_dir = S_ISDIR(buf2.st_mode) != 0;
  434.     else
  435.         *stalled_link = 1;
  436.     }
  437.  
  438.     /* Need to grow the *list? */
  439.     if (next_free == list->size){
  440.     list->list = realloc (list->list, sizeof (file_entry) *
  441.                   (list->size + RESIZE_STEPS));
  442.     if (!list->list)
  443.         return -1;
  444.     list->size += RESIZE_STEPS;
  445.     }
  446.     return 1;
  447. }
  448.  
  449. int do_load_dir(dir_list *list, sortfn *sort, int reverse, int case_sensitive, char *filter)
  450. {
  451.     DIR           *dirp;
  452.     struct dirent *dp;
  453.     int           status, link_to_dir, stalled_link;
  454.     int           next_free = 0;
  455.     struct stat   buf;
  456.     int dotdot_found = 0;
  457.  
  458.     start_tree_check (NULL);
  459.     
  460.     dirp = mc_opendir (".");
  461.     if (!dirp){
  462.     return set_zero_dir (list);
  463.     }
  464.     for (dp = mc_readdir (dirp); dp; dp = mc_readdir (dirp)){
  465.     status = handle_dirent (list, filter, dp, &buf, next_free, &link_to_dir,
  466.         &stalled_link);
  467.     if (status == 0)
  468.         continue;
  469.     if (status == -1)
  470.         return next_free;
  471.     list->list [next_free].fnamelen = NLENGTH (dp);
  472.     list->list [next_free].fname = strdup (dp->d_name);
  473.     list->list [next_free].f.marked = 0;
  474.     list->list [next_free].f.link_to_dir = link_to_dir;
  475.     list->list [next_free].f.stalled_link = stalled_link;
  476.         list->list [next_free].f.dir_size_computed = 0;
  477.     list->list [next_free].buf = buf;
  478.     if (strcmp (dp->d_name, ".." ) == 0)
  479.         dotdot_found = 1;
  480.     next_free++;
  481.     if (!(next_free % 32))
  482.         rotate_dash ();
  483.     }
  484.  
  485.     if (next_free) {
  486.     if (!dotdot_found)
  487.         add_dotdot_to_list (list, next_free++);
  488.     do_sort (list, sort, next_free-1, reverse, case_sensitive);
  489.     }
  490.     else
  491.     return set_zero_dir (list);
  492.     
  493.     mc_closedir (dirp);
  494.     end_tree_check (NULL);
  495.     return next_free;
  496. }
  497.  
  498. int link_isdir (file_entry *file)
  499. {
  500.     if (file->f.link_to_dir)
  501.     return 1;
  502.     else
  503.     return 0;
  504. }
  505.  
  506. int if_link_is_exe (file_entry *file)
  507. {
  508.     struct stat b;
  509.  
  510.     if (S_ISLNK (file->buf.st_mode)){
  511.     mc_stat (file->fname, &b);
  512.     return is_exe (b.st_mode);
  513.     }
  514.     return 1;
  515. }
  516.  
  517. static dir_list dir_copy = { 0, 0 };
  518.  
  519. static void alloc_dir_copy (int size)
  520. {
  521.     int i;
  522.         
  523.     if (dir_copy.size < size){
  524.     if (dir_copy.list){
  525.  
  526.         for (i = 0; i < dir_copy.size; i++) {
  527.         if (dir_copy.list [i].fname)
  528.             free (dir_copy.list [i].fname);
  529.         }
  530.         free (dir_copy.list);
  531.         dir_copy.list = 0;
  532.     }
  533.  
  534.     dir_copy.list = xmalloc (sizeof (file_entry) * size, "alloc_dir_copy");
  535.     for (i = 0; i < size; i++)
  536.         dir_copy.list [i].fname = 0;
  537.  
  538.     dir_copy.size = size;
  539.     }
  540. }
  541.  
  542. /* If filter is null, then it is a match */
  543. int do_reload_dir (dir_list *list, sortfn *sort, int count, int rev,
  544.            int case_sensitive, char *filter)
  545. {
  546.     DIR           *dirp;
  547.     struct dirent *dp;
  548.     int           next_free = 0;
  549.     int           i, found, status, link_to_dir, stalled_link;
  550.     struct stat   buf;
  551.     int          tmp_len;  /* For optimisation */
  552.     int       dotdot_found = 0; 
  553.  
  554.     start_tree_check (NULL);
  555.     dirp = mc_opendir (".");
  556.     if (!dirp) {
  557.      clean_dir (list, count);
  558.     return set_zero_dir (list);
  559.     }
  560.  
  561.     alloc_dir_copy (list->size);
  562.     for (i = 0; i < count; i++){
  563.     dir_copy.list [i].fnamelen = list->list [i].fnamelen;
  564.     dir_copy.list [i].fname =    list->list [i].fname;
  565.     dir_copy.list [i].f.marked = list->list [i].f.marked;
  566.         dir_copy.list [i].f.dir_size_computed = list->list [i].f.dir_size_computed;
  567.     dir_copy.list [i].f.link_to_dir = list->list [i].f.link_to_dir;
  568.     dir_copy.list [i].f.stalled_link = list->list [i].f.stalled_link;
  569.     }
  570.  
  571.     for (dp = mc_readdir (dirp); dp; dp = mc_readdir (dirp)){
  572.     status = handle_dirent (list, filter, dp, &buf, next_free, &link_to_dir,
  573.         &stalled_link);
  574.     if (status == 0)
  575.         continue;
  576.     if (status == -1) {
  577.         mc_closedir (dirp);
  578.         /* Norbert (Feb 12, 1997): 
  579.          Just in case someone finds this memory leak:
  580.          -1 means big trouble (at the moment no memory left), 
  581.          I don't bother with further cleanup because if one gets to
  582.          this point he will have more problems than a few memory
  583.          leaks and because one 'clean_dir' would not be enough (and
  584.          because I don't want to spent the time to make it working, 
  585.              IMHO it's not worthwhile). 
  586.         clean_dir (&dir_copy, count);
  587.              */
  588.         return next_free;
  589.     }
  590.     
  591.     tmp_len = NLENGTH (dp);
  592.     for (found = i = 0; i < count; i++)
  593.         if (tmp_len == dir_copy.list [i].fnamelen
  594.         && !strcmp (dp->d_name, dir_copy.list [i].fname)){
  595.         list->list [next_free].f.marked = dir_copy.list [i].f.marked;
  596.                 found = 1;
  597.         break;
  598.         }
  599.     
  600.     if (!found)
  601.         list->list [next_free].f.marked = 0;
  602.     
  603.     list->list [next_free].fnamelen = tmp_len;
  604.     list->list [next_free].fname = strdup (dp->d_name);
  605.     list->list [next_free].f.link_to_dir = link_to_dir;
  606.     list->list [next_free].f.stalled_link = stalled_link;
  607.         list->list [next_free].f.dir_size_computed = 0;
  608.     list->list [next_free].buf = buf;
  609.     if (strcmp (dp->d_name, ".." ) == 0)
  610.         dotdot_found = 1;
  611.     next_free++;
  612.     if (!(next_free % 16))
  613.         rotate_dash ();
  614.     }
  615.     mc_closedir (dirp);
  616.     end_tree_check (NULL);
  617.     if (next_free) {
  618.     if (!dotdot_found)
  619.         add_dotdot_to_list (list, next_free++);
  620.     do_sort (list, sort, next_free-1, rev, case_sensitive);
  621.     }
  622.     else
  623.     next_free = set_zero_dir (list);
  624.     clean_dir (&dir_copy, count);
  625.     return next_free;
  626. }
  627.  
  628. char *sort_type_to_name (sortfn *sort_fn)
  629. {
  630.     int i;
  631.  
  632.     for (i = 0; i < SORT_TYPES; i++)
  633.     if ((sortfn *) (sort_orders [i].sort_fn) == sort_fn)
  634.         return _(sort_orders [i].sort_name);
  635.  
  636.     return _("Unknown");
  637. }
  638.  
  639. sortfn *sort_name_to_type (char *sname)
  640. {
  641.     int i;
  642.  
  643.     for (i = 0; i < SORT_TYPES; i++)
  644.     if (strcasecmp (sort_orders [i].sort_name, sname) == 0)
  645.         return (sortfn *) sort_orders [i].sort_fn;
  646.  
  647.     /* default case */
  648.     return (sortfn *) sort_name;
  649. }
  650.  
  651.