home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / git-4.3 / git-4 / git-4.3.11 / src / panel.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-02  |  74.9 KB  |  3,624 lines

  1. /* panel.c -- The panels management file.  */
  2.  
  3. /* Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
  4.  
  5.    This program is free software; you can redistribute it and/or modify
  6.    it under the terms of the GNU General Public License as published by
  7.    the Free Software Foundation; either version 2, or (at your option)
  8.    any later version.
  9.  
  10.    This program is distributed in the hope that it will be useful,
  11.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.    GNU General Public License for more details.
  14.  
  15.    You should have received a copy of the GNU General Public License
  16.    along with this program; if not, write to the Free Software
  17.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  18.  
  19. /* Written by Tudor Hulubei and Andrei Pitis.  */
  20.  
  21.  
  22. #ifdef HAVE_CONFIG_H
  23. #include <config.h>
  24. #endif
  25.  
  26. #include <stdio.h>
  27.  
  28. #ifdef HAVE_STDLIB_H
  29. #include <stdlib.h>
  30. #else /* !HAVE_STDLIB_H */
  31. #include "ansi_stdlib.h"
  32. #endif /* !HAVE_STDLIB_H */
  33.  
  34. #include <sys/types.h>
  35. #include <ctype.h>
  36. #include "file.h"
  37.  
  38. #ifdef HAVE_VALUES_H
  39. #include <values.h>
  40. #endif /* HAVE_VALUES_H */
  41.  
  42. #include <limits.h>
  43.  
  44. #ifndef INT_MAX
  45. /* The actual value doesn't matter too much... */
  46. #define INT_MAX 32767
  47. #endif /* INT_MAX */
  48.  
  49. #ifdef HAVE_UNISTD_H
  50. #include <unistd.h>
  51. #endif /* HAVE_UNISTD_H */
  52.  
  53. #include "xtime.h"
  54.  
  55. #include <errno.h>
  56.  
  57. /* Not all systems declare ERRNO in errno.h... and some systems #define it! */
  58. #if !defined (errno)
  59. extern int errno;
  60. #endif /* !errno */
  61.  
  62. /* Get the statfs() prototipe from sys/vfs.h.  */
  63. #ifdef HAVE_LINUX
  64. #include <sys/vfs.h>
  65. #endif  /* HAVE_LINUX */
  66.  
  67. #include "stdc.h"
  68. #include "xstring.h"
  69. #include "xmalloc.h"
  70. #include "xio.h"
  71. #include "xid.h"
  72. #include "fsusage.h"
  73. #include "window.h"
  74. #include "status.h"
  75. #include "signals.h"
  76. #include "tty.h"
  77. #include "inputline.h"
  78. #include "panel.h"
  79. #include "tilde.h"
  80. #include "fnmatch.h"
  81. #include "configure.h"
  82. #include "system.h"
  83. #include "misc.h"
  84. #include "stat.h"
  85.  
  86.  
  87. extern int signals_status;
  88. extern int AnsiColors;
  89. extern int TypeSensitivity;
  90.  
  91.  
  92. char bad_name[] = "***** Invalid name ! *****";
  93. char rights[16] = "-rwxrwxrwx";
  94.  
  95.  
  96. #define FILE_DISPLAY_MODES      6
  97.  
  98. char *FileDisplayMode[FILE_DISPLAY_MODES] =
  99. {
  100.     "OwnerGroup",
  101.     "DateTime",
  102.     "Size",
  103.     "Mode",
  104.     "FullName",
  105.     "All",
  106. };
  107.  
  108.  
  109. #define FILE_SORT_METHODS       9
  110.  
  111. char *FileSortMethod[FILE_SORT_METHODS] =
  112. {
  113.     "Name",
  114.     "Extension",
  115.     "Size",
  116.     "Date",
  117.     "Mode",
  118.     "OwnerId",
  119.     "GroupId",
  120.     "OwnerName",
  121.     "GroupName",
  122. };
  123.  
  124.  
  125. #define PANEL_FIELDS    17
  126.  
  127. static char *PanelFields[PANEL_FIELDS] =
  128. {
  129.     "PanelFrame",
  130.     "PanelBackground",
  131.     "PanelSelectedFile",
  132.     "PanelSelectedFileBrightness",
  133.     "PanelNotSelectedFile",
  134.     "PanelNotSelectedFileBrightness",
  135.     "PanelCurrentSelectedFile",
  136.     "PanelCurrentNotSelectedFile",
  137.     "PanelCurrentFile",
  138.     "PanelPath",
  139.     "PanelPathBrightness",
  140.     "PanelDeviceFreeSpace",
  141.     "PanelDeviceFreeSpaceBrightness",
  142.     "PanelFileInfo",
  143.     "PanelFileInfoBrightness",
  144.     "PanelFilesInfo",
  145.     "PanelFilesInfoBrightness",
  146. };
  147.  
  148. #ifdef HAVE_LINUX
  149. static int PanelColors[PANEL_FIELDS] =
  150. {
  151.     WHITE, BLUE, YELLOW, ON, WHITE, ON, YELLOW, WHITE,
  152.     CYAN, RED, OFF, RED, OFF, RED, OFF, BLACK, OFF,
  153. };
  154. #else   /* !HAVE_LINUX */
  155. static int PanelColors[PANEL_FIELDS] =
  156. {
  157.     WHITE, BLACK, WHITE, ON, WHITE, OFF, WHITE, BLACK,
  158.     WHITE, BLACK, OFF, BLACK, OFF, BLACK, OFF, BLACK, OFF
  159. };
  160. #endif  /* !HAVE_LINUX */
  161.  
  162. #define PanelFrame                      PanelColors[0]
  163. #define PanelBackground                 PanelColors[1]
  164. #define PanelSelectedFile               PanelColors[2]
  165. #define PanelSelectedFileBrightness     PanelColors[3]
  166. #define PanelNotSelectedFile            PanelColors[4]
  167. #define PanelNotSelectedFileBrightness  PanelColors[5]
  168. #define PanelCurrentSelectedFile        PanelColors[6]
  169. #define PanelCurrentNotSelectedFile     PanelColors[7]
  170. #define PanelCurrentFile                PanelColors[8]
  171. #define PanelPath                       PanelColors[9]
  172. #define PanelPathBrightness             PanelColors[10]
  173. #define PanelDeviceFreeSpace            PanelColors[11]
  174. #define PanelDeviceFreeSpaceBrightness  PanelColors[12]
  175. #define PanelFileInfo                   PanelColors[13]
  176. #define PanelFileInfoBrightness         PanelColors[14]
  177. #define PanelFilesInfo                  PanelColors[15]
  178. #define PanelFilesInfoBrightness        PanelColors[16]
  179.  
  180.  
  181. char *panel_il_message[] =
  182. {
  183.     "Wait, reading directory...",
  184.     "Wait, copying file...",
  185.     "Wait, copying file(s)...",
  186.     "Wait, copying directory...",
  187.     "Wait, deleting directory...",
  188.     "Wait, deleting file(s)...",
  189.     "Wait, moving file...",
  190.     "Wait, moving file(s)...",
  191.     "Wait, moving directory...",
  192. };
  193.  
  194. #define PANEL_READ_DIR_MSG      panel_il_message[0]
  195. #define PANEL_COPY_FILE_MSG     panel_il_message[1]
  196. #define PANEL_COPY_FILES_MSG    panel_il_message[2]
  197. #define PANEL_COPY_DIR_MSG      panel_il_message[3]
  198. #define PANEL_DELETE_DIR_MSG    panel_il_message[4]
  199. #define PANEL_DELETE_FILES_MSG  panel_il_message[5]
  200. #define PANEL_MOVE_FILE_MSG     panel_il_message[6]
  201. #define PANEL_MOVE_FILES_MSG    panel_il_message[7]
  202. #define PANEL_MOVE_DIR_MSG      panel_il_message[8]
  203.  
  204.  
  205. /* Some useful isearch stack management functions.  */
  206.  
  207. #define STACK_PUSH(__entry__, __length__)                       \
  208. {                                                               \
  209.     isearch_t current_isearch;                                  \
  210.                                 \
  211.     current_isearch.entry  = __entry__;                         \
  212.     current_isearch.length = __length__;                        \
  213.                                 \
  214.     xstack_push(this->isearch_stack, ¤t_isearch);         \
  215. }
  216.  
  217.  
  218. #define STACK_POP(__entry__, __length__)                        \
  219. {                                                               \
  220.     isearch_t tmp_isearch;                                      \
  221.                                 \
  222.     xstack_pop(this->isearch_stack, &tmp_isearch);              \
  223.                                 \
  224.     __entry__  = tmp_isearch.entry;                             \
  225.     __length__ = tmp_isearch.length;                            \
  226. }
  227.  
  228.  
  229. #define STACK_PREVIEW(__entry__, __length__)                    \
  230. {                                                               \
  231.     isearch_t tmp_isearch;                                      \
  232.                                 \
  233.     xstack_preview(this->isearch_stack, &tmp_isearch, 1);       \
  234.                                 \
  235.     __entry__  = tmp_isearch.entry;                             \
  236.     __length__ = tmp_isearch.length;                            \
  237. }
  238.  
  239.  
  240. /* Is the current directory the root directory ?  */
  241. #define rootdir()       (this->path[0] == '/' && this->path[1] == 0)
  242.  
  243.  
  244. static int StartupFileDisplayMode;
  245. static int StartupFileSortMethod;
  246. static int StartupScrollStep;
  247. static int CurrentSortMethod;
  248. static int LeadingDotMatch = OFF;
  249. static int InfoDisplay     = OFF;
  250.  
  251. static char nice_try[] = "Nice try, maybe later... :-)";
  252.  
  253. void fatal PROTO ((char *));
  254. void panel_update_entry PROTO ((panel_t *, int));
  255. char  il_read_char PROTO ((char *, char *, int));
  256. char *il_read_line PROTO ((char *, char **, char *, xstack_t *));
  257.  
  258.  
  259. static xstack_t *copy_history;
  260. static xstack_t *move_history;
  261. static xstack_t *mkdir_history;
  262.  
  263.  
  264. static void
  265. xchg(a, b)
  266.     int *a, *b;
  267. {
  268.     int tmp = *a;
  269.     *a = *b;
  270.     *b = tmp;
  271. }
  272.  
  273.  
  274. panel_t *
  275. panel_init(x, y, lines, columns, path)
  276.     int x, y;
  277.     int lines, columns;
  278.     char *path;
  279. {
  280.     static int configured;
  281.  
  282.     panel_t *this = (panel_t *)xmalloc(sizeof(panel_t));
  283.  
  284.     this->lines           = lines;
  285.     this->columns         = columns;
  286.     this->x               = x;
  287.     this->y               = y;
  288.     this->focus           = OFF;
  289.     this->entries         = 0;
  290.     this->selected_files  = 0;
  291.     this->last_index      = -1;
  292.     this->display_mode    = this->sort_method = 0;
  293.     this->current_entry   = 0;
  294.     this->first_on_screen = 0;
  295.     this->on_screen       = INT_MAX / 2;
  296.     this->temp            = xmalloc(this->columns);
  297.     this->dir             = NULL;
  298.     this->isearch_stack   = NULL;
  299.     this->visible         = 0;
  300.  
  301. #ifdef HAVE_LINUX
  302.     this->msdosfs = 0;
  303. #endif  /* HAVE_LINUX */
  304.  
  305.     this->dir_entry = NULL;
  306.  
  307.     if (chdir(path) == -1)
  308.     fatal("`chdir' failed: permission denied");
  309.  
  310.     this->path = xgetcwd();
  311.  
  312.     if (this->path == NULL)
  313.     fatal("`getcwd' failed: permission denied");
  314.  
  315.     clear_path(this->path);
  316.     this->pathlen = strlen(this->path);
  317.  
  318.     this->window = window_init(this->x, this->y, this->lines, this->columns);
  319.  
  320.     if (configured)
  321.     {
  322.     this->display_mode = StartupFileDisplayMode;
  323.     this->sort_method  = StartupFileSortMethod;
  324.     this->scroll_step  = StartupScrollStep;
  325.     return this;
  326.     }
  327.  
  328.  
  329.     use_section("[Setup]");
  330.  
  331.     StartupScrollStep = get_int_var("StartupScrollStep", this->lines / 2);
  332.  
  333.     if (StartupScrollStep <= 0 || StartupScrollStep >= this->lines - 1)
  334.     StartupScrollStep = this->lines / 2;
  335.  
  336.     this->scroll_step = StartupScrollStep;
  337.  
  338.  
  339.     use_section("[GIT-Setup]");
  340.  
  341.     StartupFileDisplayMode = get_const_var("StartupFileDisplayMode",
  342.                         FileDisplayMode,
  343.                         FILE_DISPLAY_MODES, 0);
  344.     this->display_mode = StartupFileDisplayMode;
  345.  
  346.     StartupFileSortMethod = get_const_var("StartupFileSortMethod",
  347.                        FileSortMethod,
  348.                        FILE_SORT_METHODS, 0);
  349.     this->sort_method = StartupFileSortMethod;
  350.  
  351.     InfoDisplay     = get_flag_var("InfoDisplay",     ON);
  352.     LeadingDotMatch = get_flag_var("LeadingDotMatch", ON);
  353.  
  354.  
  355.     use_section(AnsiColors ? cSection : bwSection);
  356.  
  357.     get_colorset_var(PanelColors, PanelFields, PANEL_FIELDS);
  358.  
  359.  
  360.     copy_history  = xstack_init(sizeof(char *));
  361.     move_history  = xstack_init(sizeof(char *));
  362.     mkdir_history = xstack_init(sizeof(char *));
  363.  
  364.     configured = 1;
  365.     return this;
  366. }
  367.  
  368.  
  369. void
  370. panel_end(this)
  371.     panel_t *this;
  372. {
  373.     int i;
  374.  
  375.     if (this->dir)
  376.     closedir(this->dir);
  377.  
  378.     for (i = 0; i < this->entries; i++)
  379.     if (this->dir_entry[i].name)
  380.         xfree(this->dir_entry[i].name);
  381.  
  382.     xfree(this->dir_entry);
  383.     xfree(this->temp);
  384.  
  385.     window_end(this->window);
  386.  
  387.     xfree(this);
  388. }
  389.  
  390.  
  391. static int
  392. get_fos(this)
  393.     panel_t *this;
  394. {
  395.     return max(0, this->current_entry - (this->lines - 2) + 1);
  396. }
  397.  
  398.  
  399. static int
  400. get_centered_fos(this)
  401.     panel_t *this;
  402. {
  403.     int lines = (this->lines - 2);
  404.     int tmp = this->current_entry - (lines >> 1);
  405.  
  406.     if (tmp + lines >= this->entries)
  407.     return max(0, this->entries - lines);
  408.     else
  409.     return max(0, tmp);
  410. }
  411.  
  412.  
  413. /* We might need to call this twice in the same expression, so this is
  414.    the reason for having tname[2].  If filled != 0, fill the resulting
  415.    string with spaces up to 14 characters.  */
  416.  
  417. static char *
  418. cutname(name, which, filled)
  419.     char *name;
  420.     int which, filled;
  421. {
  422.     static char tname[2][16];
  423.  
  424.     if (filled)
  425.     {
  426.     memset(tname[which], ' ', 14);
  427.     tname[which][14] = 0;
  428.     return memcpy(tname[which], name, min(strlen(name), 14));
  429.     }
  430.     else
  431.     return strncpy(tname[which], name, 14);
  432. }
  433.  
  434.  
  435. static int
  436. qcompare(_first, _second)
  437.     const void *_first;
  438.     const void *_second;
  439. {
  440.     int retval;
  441.     char *pfirst, *psecond;
  442.     const dir_entry_t *first  = (const dir_entry_t *)_first;
  443.     const dir_entry_t *second = (const dir_entry_t *)_second;
  444.     int first_is_dir  = first->type  == DIR_ENTRY;
  445.     int second_is_dir = second->type == DIR_ENTRY;
  446.  
  447.     if (first_is_dir != second_is_dir)
  448.     return first_is_dir ? -1 : 1;
  449.  
  450.     switch (CurrentSortMethod)
  451.     {
  452.      case SORT_BY_NAME:
  453.  
  454.          pfirst = psecond = NULL;
  455.          return strcmp(first->name, second->name);
  456.  
  457.      case SORT_BY_EXTENSION:
  458.  
  459.          pfirst  = strrchr(first->name,  '.');
  460.          psecond = strrchr(second->name, '.');
  461.  
  462.          if (pfirst && psecond)
  463.          return (retval = strcmp(++pfirst, ++psecond)) ?
  464.              retval : strcmp(first->name, second->name);
  465.          else
  466.          return (pfirst || psecond) ?
  467.             (pfirst ? -1 : 1) : strcmp(first->name, second->name);
  468.          break;
  469.  
  470.      case SORT_BY_SIZE:
  471.  
  472.          if (first->size == second->size)
  473.          return strcmp(first->name, second->name);
  474.          return first->size - second->size;
  475.  
  476.      case SORT_BY_DATE:
  477.  
  478.          if (first->mtime == second->mtime)
  479.          return strcmp(first->name, second->name);
  480.          return first->mtime - second->mtime;
  481.  
  482.      case SORT_BY_MODE:
  483.  
  484.          if (first->mode == second->mode)
  485.          return strcmp(first->name, second->name);
  486.          return first->mode - second->mode;
  487.  
  488.      case SORT_BY_OWNER_ID:
  489.  
  490.          if (first->uid == second->uid)
  491.          return strcmp(first->name, second->name);
  492.          return first->uid - second->uid;
  493.  
  494.      case SORT_BY_GROUP_ID:
  495.  
  496.          if (first->gid == second->gid)
  497.          return strcmp(first->name, second->name);
  498.          return first->gid - second->gid;
  499.  
  500.      case SORT_BY_OWNER_NAME:
  501.  
  502.          if (first->uid == second->uid)
  503.          return strcmp(first->name, second->name);
  504.          return strcmp(first->owner, second->owner);
  505.  
  506.      case SORT_BY_GROUP_NAME:
  507.  
  508.          if (first->gid == second->gid)
  509.          return strcmp(first->name, second->name);
  510.          return strcmp(first->group, second->group);
  511.  
  512.      default:
  513.          fatal("bad sort method");
  514.     }
  515.  
  516.     /* not reached. */
  517.     return 0;
  518. }
  519.  
  520.  
  521. void
  522. panel_no_optimizations(this)
  523.     panel_t *this;
  524. {
  525.     this->on_screen = INT_MAX / 2;
  526. }
  527.  
  528.  
  529. char *
  530. panel_get_current_file_name(this)
  531.     panel_t *this;
  532. {
  533.     return this->dir_entry[this->current_entry].name;
  534. }
  535.  
  536.  
  537. uid_t
  538. panel_get_current_file_uid(this)
  539.     panel_t *this;
  540. {
  541.     return this->dir_entry[this->current_entry].uid;
  542. }
  543.  
  544.  
  545. gid_t
  546. panel_get_current_file_gid(this)
  547.     panel_t *this;
  548. {
  549.     return this->dir_entry[this->current_entry].gid;
  550. }
  551.  
  552.  
  553. mode_t
  554. panel_get_current_file_mode(this)
  555.     panel_t *this;
  556. {
  557.     return this->dir_entry[this->current_entry].mode;
  558. }
  559.  
  560.  
  561. int
  562. panel_get_current_file_type(this)
  563.     panel_t *this;
  564. {
  565.     return this->dir_entry[this->current_entry].type;
  566. }
  567.  
  568.  
  569. void
  570. panel_set_position(this, entry)
  571.     panel_t *this;
  572.     int entry;
  573. {
  574.     this->current_entry   = entry;
  575.     this->first_on_screen = get_fos(this);
  576. }
  577.  
  578.  
  579. void
  580. panel_activate(this)
  581.     panel_t *this;
  582. {
  583.     this->visible = 0;
  584. }
  585.  
  586.  
  587. void
  588. panel_deactivate(this)
  589.     panel_t *this;
  590. {
  591.     this->visible = 1;
  592. }
  593.  
  594.  
  595. void
  596. panel_set_wrapped_isearch_flag(this)
  597.     panel_t *this;
  598. {
  599.     this->wrapped_isearch = 1;
  600. }
  601.  
  602.  
  603. int
  604. panel_isearch_backward(this, string, len, start_entry)
  605.     panel_t *this;
  606.     char *string;
  607.     size_t len;
  608.     int start_entry;
  609. {
  610.     int i;
  611.  
  612.     for (i = start_entry; i >= 0; i--)
  613.     {
  614.     if (strncasecmp(string, this->dir_entry[i].name, len) == 0)
  615.     {
  616.         /* Success, return the entry just found.  */
  617.         return i;
  618.     }
  619.     }
  620.  
  621.     /* Error, cannot find matching entry.  */
  622.     return -1;
  623. }
  624.  
  625.  
  626. int
  627. panel_isearch_forward(this, string, len, start_entry)
  628.     panel_t *this;
  629.     char *string;
  630.     size_t len;
  631.     int start_entry;
  632. {
  633.     int i;
  634.  
  635.     for (i = start_entry; i < this->entries; i++)
  636.     {
  637.     if (strncasecmp(string, this->dir_entry[i].name, len) == 0)
  638.     {
  639.         /* Success, return the entry just found.  */
  640.         return i;
  641.     }
  642.     }
  643.  
  644.     /* Error, cannot find matching entry.  */
  645.     return -1;
  646. }
  647.  
  648.  
  649. #define panel_1s_message il_read_char
  650.  
  651.  
  652. char
  653. panel_2s_message(format, string, options, flags)
  654.     char *format;
  655.     char *string;
  656.     char *options;
  657.     int flags;
  658. {
  659.     char c;
  660.     char *message = xmalloc(strlen(format) + strlen(string) + 1);
  661.  
  662.     sprintf(message, format, string);
  663.     c = panel_1s_message(message, options, flags);
  664.     xfree(message);
  665.     return c;
  666. }
  667.  
  668.  
  669. char
  670. panel_3s_message(format, string1, string2, options, flags)
  671.     char *format;
  672.     char *string1;
  673.     char *string2;
  674.     char *options;
  675.     int flags;
  676. {
  677.     char c;
  678.     char *message = xmalloc(strlen(format)+strlen(string1)+strlen(string2)+1);
  679.  
  680.     sprintf(message, format, string1, string2);
  681.     c = panel_1s_message(message, options, flags);
  682.     xfree(message);
  683.     return c;
  684. }
  685.  
  686.  
  687. void
  688. panel_recover(this)
  689.     panel_t *this;
  690. {
  691.     this->first_on_screen = this->current_entry = 0;
  692.  
  693.     panel_2s_message("%s/: Permission denied.",
  694.              this->path, NULL, IL_MOVE | IL_BEEP | IL_SAVE | IL_ERROR);
  695.  
  696.     if (strcmp(this->path, "/") == 0)
  697.     fatal("/: Permission denied");
  698.  
  699.     strcpy(this->path, "/");
  700.     this->pathlen = 1;
  701.     chdir(this->path);
  702.     panel_action(this, act_REFRESH, NULL, NULL, 1);
  703. }
  704.  
  705.  
  706. static void
  707. load_fti(this, entry)
  708.     panel_t *this;
  709.     int entry;
  710. {
  711.     file_type_info_t *fti;
  712.  
  713.     this->dir_entry[entry].fti_loaded = 1;
  714.  
  715.     for (fti = fti_head; fti; fti = fti->next)
  716.     if (fnmatch(fti->regexp, this->dir_entry[entry].name,
  717.             FNM_PERIOD | FNM_PATHNAME | FNM_CASEFOLD) == 0)
  718.     {
  719.         this->dir_entry[entry].brightness =
  720.         (fti->brightness == -1) ? OFF : fti->brightness;
  721.  
  722.         this->dir_entry[entry].foreground =
  723.         (fti->foreground == -1) ? PanelNotSelectedFile:fti->foreground;
  724.  
  725.         this->dir_entry[entry].background =
  726.         (fti->background == -1) ? PanelBackground : fti->background;
  727.  
  728.         return;
  729.     }
  730.  
  731.     /* No match.  An invalid value of the brightness field specifies it.
  732.        It's ugly, but there are no more free bits in dir_entry_t.  */
  733.     this->dir_entry[entry].brightness = 2;
  734. }
  735.  
  736.  
  737. /*
  738.  * Read the inode information.
  739.  */
  740.  
  741. static void
  742. load_inode(this, entry)
  743.     panel_t *this;
  744.     int entry;
  745. {
  746.     int sz, hour;
  747.     struct stat s;
  748.     struct tm *time;
  749.  
  750.  
  751.     memset(&s, 0, sizeof(s));
  752.  
  753.     xlstat(this->dir_entry[entry].name, &s);
  754.  
  755.     this->dir_entry[entry].mode = s.st_mode;
  756.     this->dir_entry[entry].uid  = s.st_uid;
  757.     this->dir_entry[entry].gid  = s.st_gid;
  758.  
  759.     if (s.st_ino)
  760.     {
  761.     if (S_ISDIR(s.st_mode))
  762.         this->dir_entry[entry].type = DIR_ENTRY;
  763.     else
  764.     {
  765.         if (S_ISREG(s.st_mode))
  766.         {
  767.         this->dir_entry[entry].type = FILE_ENTRY;
  768. #ifdef HAVE_LINUX
  769.         /* Starting with the 0.99.13 Linux kernel all MSDOS
  770.            files have are "executables", so, when working with
  771.            msdos file systems, we have to ignore those bits ...
  772.            At least when displaying them in the panel. :-(  */
  773.         this->dir_entry[entry].executable =
  774.             ((s.st_mode & 0111) && !this->msdosfs) ? 1 : 0;
  775. #else   /* !HAVE_LINUX */
  776.         this->dir_entry[entry].executable =
  777.             (s.st_mode & 0111) ? 1 : 0;
  778. #endif  /* !HAVE_LINUX */
  779.         }
  780.         else
  781.         {
  782.         this->dir_entry[entry].executable = 0;
  783.  
  784.         if (S_ISFIFO(s.st_mode))
  785.             this->dir_entry[entry].type = FIFO_ENTRY;
  786.         else
  787.             if (S_ISSOCK(s.st_mode))
  788.             this->dir_entry[entry].type = SOCKET_ENTRY;
  789.             else
  790.             if (S_ISLNK(s.st_mode))
  791.             {
  792.                 struct stat s_tmp;
  793.                 int stat_error =
  794.                 xstat(this->dir_entry[entry].name, &s_tmp);
  795.  
  796.                 if (stat_error == -1)
  797.                 {
  798.                 /* This symbolic link has no target.  */
  799.                 this->dir_entry[entry].type =
  800.                     SYMLINK_ENTRY;
  801.                 sz = xreadlink(this->dir_entry[entry].name);
  802.                 s.st_size = (sz == -1) ? 0 : sz;
  803.                 }
  804.                 else
  805.                 {
  806.                 /* The symbolic link has a target, and we
  807.                    are going to display the target size,
  808.                    not the link one.  Feel free to blame
  809.                    me for this.  :-) */
  810.                 this->dir_entry[entry].type =
  811.                     S_ISDIR(s_tmp.st_mode) ?  DIR_ENTRY :
  812.                                  FILE_ENTRY;
  813.                 s.st_size = s_tmp.st_size;
  814.                 /* Also take care if the link target is
  815.                    an executable.  */
  816.                 this->dir_entry[entry].executable =
  817.                     (s_tmp.st_mode & 0111) ? 1 : 0;
  818.                 }
  819.             }
  820.             else
  821.                 this->dir_entry[entry].type = FILE_ENTRY;
  822.         }
  823.     }
  824.     this->dir_entry[entry].size = s.st_size;
  825.     }
  826.     else
  827.     {
  828.     /* I think this is obsolete.  But I am keeping this it just in
  829.        case something wrong happens... :-) I think I've covered
  830.        symbolic links completely on the true branch of this if ()
  831.        statement.  However, if s.st_mode == 0, we might reach this
  832.        point.  */
  833.     this->dir_entry[entry].type = SYMLINK_ENTRY;
  834.     sz = xreadlink(this->dir_entry[entry].name);
  835.     this->dir_entry[entry].size = (sz == -1) ? 0 : sz;
  836.     }
  837.  
  838.     this->dir_entry[entry].owner = xgetpwuid(s.st_uid);
  839.     this->dir_entry[entry].group = xgetgrgid(s.st_gid);
  840.  
  841.     this->dir_entry[entry].mtime = s.st_mtime;
  842.     time = localtime(&s.st_mtime);
  843.  
  844.     if ((hour = time->tm_hour % 12) == 0)
  845.     hour = 12;
  846.  
  847.     sprintf(this->dir_entry[entry].date,"%2d-%02d-%02d %2d:%02d%c",
  848.         time->tm_mon + 1, time->tm_mday, time->tm_year % 100,
  849.         hour, time->tm_min, (time->tm_hour < 12) ? 'a' : 'p');
  850. }
  851.  
  852.  
  853. int
  854. panel_read_directory(this, directory, verify)
  855.     panel_t *this;
  856.     char *directory;
  857.     int verify;
  858. {
  859. #ifdef HAVE_LINUX
  860.     struct statfs fstat;
  861. #endif  /* HAVE_LINUX */
  862.  
  863.     DIR *tmpdir;
  864.     struct stat s;
  865.     size_t namelen;
  866.     char *old_path;
  867.     struct dirent *d;
  868.     int dotdot_found = 0;
  869.     dir_entry_t *old_dir_entry = NULL, tmp;
  870.     int i, j, old_entries = 0, backdir_index = -1;
  871.  
  872.  
  873.     tmpdir = opendir(directory);
  874.  
  875.     /* If the directory is on a NFS file system and we are superuser,
  876.        opendir will not fail but readdir will report 0 files :-(.  */
  877.  
  878.     if (tmpdir == NULL)
  879.     {
  880.     if (strcmp(directory, "..") != 0)
  881.         return 0;
  882.  
  883.     /* Try harder.  This migh be due to the fact that we don't
  884.        have the ".." directory (NFS).  */
  885.  
  886.     directory = xgetcwd();
  887.  
  888.     if (directory == NULL)
  889.     {
  890.         char *ptr;
  891.  
  892.         /* FIXME: This isn't always deallocated!  We should use
  893.            alloca instead...  But is ugly anyway :-( */
  894.         directory = xstrdup(this->path);
  895.  
  896.         ptr = strrchr(directory, '/');
  897.  
  898.         if (ptr == NULL)
  899.         fatal("bad directory");
  900.  
  901.         *ptr = 0;
  902.     }
  903.  
  904.     tmpdir = opendir(directory);
  905.  
  906.     if (tmpdir == NULL)
  907.     {
  908.         xfree(directory);
  909.         return 0;
  910.     }
  911.     }
  912.  
  913.     if (chdir(directory) == -1)
  914.     {
  915.     closedir(tmpdir);
  916.     return 0;
  917.     }
  918.  
  919.     if (this->dir)
  920.     closedir(this->dir);
  921.  
  922.     this->dir = tmpdir;
  923.     old_path = xmalloc(this->pathlen + 1);
  924.     strcpy(old_path, this->path);
  925.  
  926.     if (directory[0] == '/')
  927.     this->path = xstrdup(directory);
  928.     else
  929.     {
  930.     /* There is definitely a reason why this code is here, but I
  931.        don't remember it :-)  I think it might have been a problem
  932.        with entering a directory through a symbolic link which skips
  933.        a protected directory.  xgetcwd() will fail (due to the fact
  934.        that one directory in the real path is protected) and I had
  935.        to remove the last entry in the path to make it work.  */
  936.  
  937.     char *path = xgetcwd();
  938.  
  939.     if (path)
  940.     {
  941.         xfree(this->path);
  942.         this->path = path;
  943.     }
  944.     else
  945.     {
  946.         this->pathlen = strlen(this->path);
  947.  
  948.         if (strcmp(directory, "..") == 0)
  949.         {
  950.         char *ptr = strrchr(this->path, '/');
  951.  
  952.         if (ptr == NULL)
  953.             fatal("bad path");
  954.  
  955.         *ptr = 0;
  956.         }
  957.         else
  958.         {
  959.         this->path = xrealloc(this->path, this->pathlen + 1 + 1 +
  960.                       strlen(directory));
  961.         strcat(this->path, "/");
  962.         strcat(this->path, directory);
  963.          }
  964.     }
  965.     }
  966.  
  967.     clear_path(this->path);
  968.     this->pathlen = strlen(this->path);
  969.  
  970.     xstat(this->path, &s);
  971.  
  972.     if (s.st_size >= 2048)
  973.     il_message(PANEL_READ_DIR_MSG);
  974.  
  975. #ifdef HAVE_LINUX
  976.     /* I can't get this number without including linux/msdos_fs.h :-(, so
  977.        I've hard-coded it here.  */
  978.     statfs(".", &fstat);
  979.     this->msdosfs = fstat.f_type == 0x4d44;
  980. #endif /* HAVE_LINUX */
  981.  
  982.     verify = verify &&
  983.          this->selected_files &&
  984.          (strcmp(old_path, this->path) == 0);
  985.  
  986.     if (verify)
  987.     {
  988.     old_dir_entry   = this->dir_entry;
  989.     old_entries     = this->entries;
  990.     this->dir_entry = NULL;
  991.     }
  992.     else
  993.     if (this->dir_entry)
  994.     {
  995.         for (i = 0; i < this->entries; i++)
  996.         if (this->dir_entry[i].name)
  997.             xfree(this->dir_entry[i].name);
  998.  
  999.         xfree(this->dir_entry);
  1000.         this->dir_entry = NULL;
  1001.     }
  1002.  
  1003.     this->dir_entry = (dir_entry_t *)xmalloc(sizeof(dir_entry_t));
  1004.  
  1005.     for (this->selected_files = this->maxname = this->entries = 0;
  1006.      (d = readdir(this->dir));
  1007.      this->entries++)
  1008.     {
  1009.     /* Ignore the "." directory.  */
  1010.     if (d->d_name[0] == '.' && d->d_name[1] == 0)
  1011.     {
  1012.         this->entries--;
  1013.         continue;
  1014.     }
  1015.  
  1016.     if (strcmp(d->d_name, "..") == 0)
  1017.     {
  1018.         dotdot_found = 1;
  1019.  
  1020.         if (this->path[1])
  1021.         backdir_index = this->entries;
  1022.         else
  1023.         {
  1024.         /* Ignore ".." if this is the root directory.  */
  1025.         this->entries--;
  1026.         continue;
  1027.         }
  1028.     }
  1029.  
  1030.     this->dir_entry = (dir_entry_t *)xrealloc(this->dir_entry,
  1031.                           (this->entries + 1) *
  1032.                           sizeof(dir_entry_t));
  1033.     if (verify)
  1034.     {
  1035.         for (j = 0; j < old_entries; j++)
  1036.         if (strcmp(d->d_name, old_dir_entry[j].name) == 0)
  1037.         {
  1038.             this->selected_files +=
  1039.             (this->dir_entry[this->entries].selected =
  1040.              old_dir_entry[j].selected);
  1041.             break;
  1042.         }
  1043.  
  1044.         if (j == old_entries)
  1045.         this->dir_entry[this->entries].selected = 0;
  1046.     }
  1047.     else
  1048.         this->dir_entry[this->entries].selected = 0;
  1049.  
  1050.     /* Clear the fti cache flags.  */
  1051.     this->dir_entry[this->entries].fti_loaded = 0;
  1052.  
  1053.     this->dir_entry[this->entries].name =
  1054.         xmalloc((namelen = strlen(d->d_name)) + 1);
  1055.  
  1056.     strcpy(this->dir_entry[this->entries].name, d->d_name);
  1057.     this->maxname = max(this->maxname, namelen);
  1058.  
  1059.     load_inode(this, this->entries);
  1060.     }
  1061.  
  1062.     /* Consistency check.  Some NFS file systems don't have the "."
  1063.        and ".." directory entries.  */
  1064.  
  1065.     /* We should have found the ".." directory.  */
  1066.     if (dotdot_found)
  1067.     {
  1068.     if (backdir_index != -1)
  1069.         if (!S_ISDIR(this->dir_entry[backdir_index].mode))
  1070.         this->dir_entry[backdir_index].mode = S_IFDIR;
  1071.     }
  1072.     else
  1073.     {
  1074.     /* Build the ".." entry "by hand".  */
  1075.     this->dir_entry = (dir_entry_t *)xrealloc(this->dir_entry,
  1076.                           (this->entries + 1) *
  1077.                           sizeof(dir_entry_t));
  1078.  
  1079.     this->dir_entry[this->entries].selected = 0;
  1080.  
  1081.     /* Set the fti cache flags.  */
  1082.     this->dir_entry[this->entries].fti_loaded = 1;
  1083.  
  1084.     this->dir_entry[this->entries].name =
  1085.         xmalloc((namelen = strlen("..")) + 1);
  1086.  
  1087.     strcpy(this->dir_entry[this->entries].name, "..");
  1088.     this->maxname = max(this->maxname, namelen);
  1089.  
  1090.     load_inode(this, this->entries);
  1091.  
  1092.     /* This is a directory.  */
  1093.     this->dir_entry[this->entries].mode = S_IFDIR;
  1094.     this->dir_entry[this->entries].type = DIR_ENTRY;
  1095.  
  1096.     this->entries++;
  1097.     }
  1098.  
  1099.     if (verify)
  1100.     {
  1101.     for (i = 0; i < old_entries; i++)
  1102.         if (old_dir_entry[i].name)
  1103.         xfree(old_dir_entry[i].name);
  1104.  
  1105.     xfree(old_dir_entry);
  1106.     }
  1107.  
  1108.     CurrentSortMethod = this->sort_method;
  1109.  
  1110.     if (backdir_index != -1)
  1111.     {
  1112.     tmp = this->dir_entry[0];
  1113.     this->dir_entry[0] = this->dir_entry[backdir_index];
  1114.     this->dir_entry[backdir_index] = tmp;
  1115.  
  1116.     qsort(this->dir_entry + 1, this->entries - 1,
  1117.           sizeof(dir_entry_t), qcompare);
  1118.     }
  1119.     else
  1120.     qsort(this->dir_entry, this->entries, sizeof(dir_entry_t), qcompare);
  1121.  
  1122.     xfree(old_path);
  1123.     return 1;
  1124. }
  1125.  
  1126.  
  1127. void
  1128. panel_init_iterator(this)
  1129.     panel_t *this;
  1130. {
  1131.     this->last_index = -1;
  1132.     this->multiple_files = this->selected_files;
  1133. }
  1134.  
  1135.  
  1136. int
  1137. panel_get_next(this)
  1138.     panel_t *this;
  1139. {
  1140.     int i;
  1141.  
  1142.     if (this->multiple_files)
  1143.     {
  1144.     for (i = this->last_index + 1; i < this->entries; i++)
  1145.         if (this->dir_entry[i].selected)
  1146.         return this->last_index = i;
  1147.  
  1148.     return -1;
  1149.     }
  1150.     else
  1151.     {
  1152.     if (this->last_index == 0)
  1153.         return -1;
  1154.  
  1155.     this->last_index = 0;
  1156.  
  1157.     /* In the root directory there is no '..' entry, so the first
  1158.        entry can be returned.  */
  1159.     if (rootdir())
  1160.         return this->current_entry;
  1161.     else
  1162.         return (this->current_entry != 0) ? this->current_entry : -1;
  1163.     }
  1164. }
  1165.  
  1166.  
  1167. void
  1168. panel_update_entries(this)
  1169.     panel_t *this;
  1170. {
  1171.     int i, limit;
  1172.     tty_status_t status;
  1173.  
  1174.     if (this->visible)
  1175.     return;
  1176.  
  1177.     tty_save(&status);
  1178.  
  1179.     for (i = this->first_on_screen;
  1180.      i < this->entries && (i - this->first_on_screen < this->lines - 2);
  1181.      i++)
  1182.     panel_update_entry(this, i);
  1183.  
  1184.     tty_colors(OFF, WHITE, PanelBackground);
  1185.  
  1186.     memset(this->temp, ' ', this->columns);
  1187.     limit = min(this->lines - 2, this->on_screen);
  1188.  
  1189.     for (; i < limit; i++)
  1190.     {
  1191.     window_goto(this->window, i - this->first_on_screen + 1, 1);
  1192.     window_puts(this->temp, this->columns - 2);
  1193.     }
  1194.  
  1195.     this->on_screen = this->entries;
  1196.     tty_restore(&status);
  1197. }
  1198.  
  1199.  
  1200. void
  1201. panel_update_path(this)
  1202.     panel_t *this;
  1203. {
  1204.     int i;
  1205.     char *t;
  1206.     size_t len;
  1207.     tty_status_t status;
  1208.  
  1209.     if (this->visible)
  1210.     return;
  1211.  
  1212.     tty_save(&status);
  1213.  
  1214.     len = this->columns - 4 - 14 - 1;
  1215.     memset(this->temp, ' ', this->columns);
  1216.     truncate_long_name(this->path, this->temp, len);
  1217.  
  1218.     for (t = this->temp, i = 0; i < len; i++)
  1219.     if (!is_print(t[i]))
  1220.         t[i] = '?';
  1221.  
  1222.     tty_colors(PanelPathBrightness, PanelPath, PanelFrame);
  1223.  
  1224.     window_goto(this->window, 0, 2);
  1225.     window_puts(this->temp, len + 1);
  1226.  
  1227.     tty_restore(&status);
  1228. }
  1229.  
  1230.  
  1231. /*
  1232.  * Returns the beautified form of `number' in `buf'.  That is, number is
  1233.  * returned in this form, for the sake of redability: 8,881,152.  `buf' is
  1234.  * assumed to be 14 characters long.  `number' is  assumed to be < 1E11.
  1235.  */
  1236.  
  1237. static void
  1238. beautify_size_number(buf, number)
  1239.     char *buf;
  1240.     long number;
  1241. {
  1242.     int i;
  1243.  
  1244.     sprintf(buf, "%14ld", number);
  1245.  
  1246.     for (i = 10; i > 0; i -= 4)
  1247.     if (isdigit(buf[i]))
  1248.     {
  1249.         memmove(buf, buf + 1, i);
  1250.         buf[i] = ',';
  1251.     }
  1252. }
  1253.  
  1254.  
  1255. void
  1256. panel_update_size(this)
  1257.     panel_t *this;
  1258. {
  1259.     int result;
  1260.     char sz[16];
  1261.     tty_status_t status;
  1262.     struct fs_usage fsu;
  1263.  
  1264.     if (this->visible)
  1265.     return;
  1266.  
  1267.     tty_save(&status);
  1268.  
  1269.     fsu.fsu_blocks = -1;
  1270.     result = get_fs_usage(this->path, &fsu);
  1271.  
  1272.     if (result < 0 || fsu.fsu_blocks == -1)
  1273.     {
  1274.     memset(sz, ' ', 14);
  1275.  
  1276.     tty_brightness(OFF);
  1277.     tty_foreground(PanelFrame);
  1278.     }
  1279.     else
  1280.     {
  1281.     beautify_size_number(sz, fsu.fsu_bavail * 512);
  1282.  
  1283.     tty_brightness(PanelDeviceFreeSpaceBrightness);
  1284.     tty_foreground(PanelDeviceFreeSpace);
  1285.     }
  1286.  
  1287.     tty_background(PanelFrame);
  1288.  
  1289.     window_goto(this->window, 0, this->columns - 2 - 14);
  1290.     window_puts(sz, 14);
  1291.  
  1292.     tty_restore(&status);
  1293. }
  1294.  
  1295.  
  1296. static void
  1297. mode2string(this, entry, string)
  1298.     panel_t *this;
  1299.     int entry;
  1300.     char *string;
  1301. {
  1302.     int i;
  1303.     mode_t mode;
  1304.  
  1305.     strcpy(string, rights);
  1306.     mode = this->dir_entry[entry].mode;
  1307.  
  1308. #ifdef S_ISREG
  1309.     if (S_ISREG(mode))
  1310.     string[0] = '-';
  1311.     else
  1312. #endif /* S_ISREG */
  1313. #ifdef S_ISDIR
  1314.     if (S_ISDIR(mode))
  1315.         string[0] = 'd';
  1316.     else
  1317. #endif /* S_ISDIR */
  1318. #ifdef S_ISCHR
  1319.         if (S_ISCHR(mode))
  1320.         string[0] = 'c';
  1321.         else
  1322. #endif /* S_ISCHR */
  1323. #ifdef S_ISBLK
  1324.         if (S_ISBLK(mode))
  1325.             string[0] = 'b';
  1326.         else
  1327. #endif /* S_ISBLK */
  1328. #ifdef S_ISFIFO
  1329.             if (S_ISFIFO(mode))
  1330.             string[0] = 'p';
  1331.             else
  1332. #endif /* S_ISFIFO */
  1333. #ifdef S_ISSOCK
  1334.             if (S_ISSOCK(mode))
  1335.                 string[0] = 's';
  1336.             else
  1337. #endif /* S_ISSOCK */
  1338. #ifdef S_ISLNK
  1339.                 if (S_ISLNK(mode))
  1340.                 string[0] = 'l';
  1341.                 else
  1342. #endif /* S_ISLNK */
  1343.                 string[0] = '?';
  1344.  
  1345.     for (i = 0; i < 9; mode >>= 1, i++)
  1346.     if ((mode & 1) == 0)
  1347.         string[9 - i] = '-';
  1348.  
  1349.     mode = this->dir_entry[entry].mode;
  1350.  
  1351. #ifdef S_ISUID
  1352.     if (mode & S_ISUID)
  1353.     string[3] = (string[3] == 'x') ? 's' : 'S';
  1354. #endif /* S_ISUID */
  1355.  
  1356. #ifdef S_ISGID
  1357.     if (mode & S_ISGID)
  1358.     string[6] = (string[6] == 'x') ? 's' : 'S';
  1359. #endif /* S_ISGID */
  1360.  
  1361. #ifdef S_ISVTX
  1362.     if (mode & S_ISVTX)
  1363.     string[9] = (string[9] == 'x') ? 't' : 'T';
  1364. #endif /* S_ISVTX */
  1365. }
  1366.  
  1367.  
  1368. void
  1369. panel_update_info(this)
  1370.     panel_t *this;
  1371. {
  1372.     int i;
  1373.     tty_status_t status;
  1374.     char str[256], temp_rights[16], *t;
  1375.     size_t total_size = 0, len, maxname;
  1376.  
  1377.     if (this->visible)
  1378.     return;
  1379.  
  1380.     tty_save(&status);
  1381.  
  1382.     if (this->selected_files)
  1383.     {
  1384.     int offset;
  1385.     char sz[16];
  1386.  
  1387.     for (i = 0; i < this->entries; i++)
  1388.         if (this->dir_entry[i].selected)
  1389.         total_size += this->dir_entry[i].size;
  1390.  
  1391.     beautify_size_number(sz, total_size);
  1392.  
  1393.     for (offset = 0; sz[offset] == ' '; offset++);
  1394.  
  1395.     sprintf(str, "%s bytes in %d file(s)",
  1396.         &sz[offset], this->selected_files);
  1397.  
  1398.     tty_brightness(PanelFilesInfoBrightness);
  1399.     tty_foreground(PanelFilesInfo);
  1400.     }
  1401.     else
  1402.     {
  1403.     if (InfoDisplay == OFF)
  1404.     {
  1405.         *str = 0;
  1406.         goto display_info;
  1407.     }
  1408.  
  1409.     mode2string(this, this->current_entry, temp_rights);
  1410.  
  1411.     maxname = this->columns - 26;
  1412.     len = min(strlen(this->dir_entry[this->current_entry].name), maxname);
  1413.  
  1414.     memcpy(t = str, this->dir_entry[this->current_entry].name, len);
  1415.  
  1416.     for (i = 0; i < len; i++)
  1417.         if (!is_print(t[i]))
  1418.         t[i] = '?';
  1419.  
  1420.     memset(str + len, ' ', maxname - len);
  1421.  
  1422.     if (this->dir_entry[this->current_entry].type == DIR_ENTRY)
  1423.         sprintf(str + maxname, " %10s %10s",
  1424.             (strcmp(this->dir_entry[this->current_entry].name, "..") ==
  1425.              0) ?
  1426.             "UP--DIR" : "SUB-DIR", temp_rights);
  1427.     else
  1428.         sprintf(str + maxname, " %10d %10s",
  1429.             this->dir_entry[this->current_entry].size, temp_rights);
  1430.  
  1431.       display_info:
  1432.     tty_brightness(PanelFileInfoBrightness);
  1433.     tty_foreground(PanelFileInfo);
  1434.     }
  1435.  
  1436.     /* FIXME:  If I put here *str = 0; it behaves weird at ^O.  */
  1437.     memcpy(this->temp, str, len = strlen(str));
  1438.     memset(this->temp + len, ' ', this->columns - 2 - len);
  1439.     tty_background(PanelFrame);
  1440.     window_goto(this->window, this->lines - 1, 2);
  1441.     window_puts(this->temp, this->columns - 4);
  1442.  
  1443.     tty_restore(&status);
  1444. }
  1445.  
  1446.  
  1447. void
  1448. panel_build_entry_field(this, entry, display_mode, offset)
  1449.     panel_t *this;
  1450.     int entry, display_mode, offset;
  1451. {
  1452.     char buf[16], temp_rights[16];
  1453.  
  1454.     switch (display_mode)
  1455.     {
  1456.     case ENABLE_OWNER_GROUP:
  1457.  
  1458.         memcpy(this->temp + this->columns - 2 - offset,
  1459.            this->dir_entry[entry].owner, 7);
  1460.         memcpy(this->temp + this->columns - 2 - offset + 8,
  1461.            this->dir_entry[entry].group, 7);
  1462.         break;
  1463.  
  1464.     case ENABLE_DATE_TIME:
  1465.  
  1466.         memcpy(this->temp + this->columns - 2 - offset,
  1467.            this->dir_entry[entry].date, 15);
  1468.         break;
  1469.  
  1470.     case ENABLE_SIZE:
  1471.  
  1472.         sprintf(buf, "%10d", this->dir_entry[entry].size);
  1473.         memcpy(this->temp + this->columns - 2 - offset, buf, 10);
  1474.         break;
  1475.  
  1476.     case ENABLE_MODE:
  1477.  
  1478.         mode2string(this, entry, temp_rights);
  1479.  
  1480.         memcpy(this->temp + this->columns - 2 - offset, temp_rights, 10);
  1481.         break;
  1482.  
  1483.     case ENABLE_FULL_NAME:
  1484.  
  1485.         /* file name     -> 20 characters (at least)
  1486.            owner + group -> 16 characters
  1487.            date  + time  -> 16 characters
  1488.            size          -> 11 characters
  1489.            mode          -> 11 characters */
  1490.  
  1491.         if (this->columns < 20 + 16 + 16 + 11 + 11)
  1492.         break;
  1493.  
  1494.         break;
  1495.  
  1496.     case ENABLE_ALL:
  1497.  
  1498.         break;
  1499.  
  1500.     default:
  1501.  
  1502.         fatal("invalid mode");
  1503.     }
  1504. }
  1505.  
  1506.  
  1507. static int reserved_characters[FILE_DISPLAY_MODES] =
  1508. {
  1509.     1 + 1 +                16 + 1 + 1,
  1510.     1 + 1 +                16 + 1 + 1,
  1511.     1 + 1 +                11 + 1 + 1,
  1512.     1 + 1 +                11 + 1 + 1,
  1513.     1 + 1 +                 0 + 1 + 1,
  1514.     1 + 1 + 16 + 16 + 11 + 11 + 1 + 1,
  1515. };
  1516.  
  1517.  
  1518. void
  1519. panel_update_entry(this, entry)
  1520.     panel_t *this;
  1521.     int entry;
  1522. {
  1523.     int i;
  1524.     char *t;
  1525.     size_t len, reserved;
  1526.     int foreground, background, brightness;
  1527.  
  1528.  
  1529.     if (this->visible)
  1530.     return;
  1531.  
  1532.     memset(this->temp, ' ', this->columns);
  1533.  
  1534.     reserved = reserved_characters[this->display_mode];
  1535.  
  1536.     len = min(strlen(this->dir_entry[entry].name), this->columns - reserved);
  1537.  
  1538.     memcpy(t = &this->temp[1], this->dir_entry[entry].name, len);
  1539.  
  1540.     for (i = 0; i < len; i++)
  1541.     if (!is_print(t[i]))
  1542.         t[i] = '?';
  1543.  
  1544.     if (len == (unsigned)this->columns - reserved)
  1545.     len--;
  1546.  
  1547.     if (entry || this->path[1] == '\0')
  1548.     switch (this->dir_entry[entry].type)
  1549.     {
  1550.         case DIR_ENTRY:     this->temp[len + 1] = '/';
  1551.                 break;
  1552.  
  1553.         case FILE_ENTRY:    if (this->dir_entry[entry].executable)
  1554.                     this->temp[len + 1] = '*';
  1555.                 break;
  1556.  
  1557.         case SYMLINK_ENTRY: this->temp[len + 1] = '@';
  1558.                 break;
  1559.  
  1560.         case FIFO_ENTRY:    this->temp[len + 1] = '|';
  1561.                 break;
  1562.  
  1563.         case SOCKET_ENTRY:  this->temp[len + 1] = '=';
  1564.                 break;
  1565.     }
  1566.  
  1567.     switch (this->display_mode)
  1568.     {
  1569.     case ENABLE_OWNER_GROUP:
  1570.     case ENABLE_DATE_TIME:
  1571.  
  1572.         panel_build_entry_field(this, entry, this->display_mode, 16);
  1573.         break;
  1574.  
  1575.     case ENABLE_SIZE:
  1576.     case ENABLE_MODE:
  1577.  
  1578.         panel_build_entry_field(this, entry, this->display_mode, 11);
  1579.         break;
  1580.  
  1581.     case ENABLE_FULL_NAME:
  1582.  
  1583.         /* Don't call panel_build_entry_field(), is useless.  */
  1584.         break;
  1585.  
  1586.     case ENABLE_ALL:
  1587.  
  1588.         /* file name     -> 20 characters (at least)
  1589.            owner + group -> 16 characters
  1590.            date  + time  -> 16 characters
  1591.            size          -> 11 characters
  1592.            mode          -> 11 characters */
  1593.  
  1594.         if (this->columns < 20 + 16 + 16 + 11 + 11)
  1595.         break;
  1596.  
  1597.         panel_build_entry_field(this, entry, ENABLE_OWNER_GROUP,
  1598.                     16 + 16 + 11 + 11);
  1599.         panel_build_entry_field(this, entry, ENABLE_DATE_TIME,
  1600.                     16 + 11 + 11);
  1601.         panel_build_entry_field(this, entry, ENABLE_SIZE,
  1602.                     11 + 11);
  1603.         panel_build_entry_field(this, entry, ENABLE_MODE,
  1604.                     11);
  1605.         break;
  1606.  
  1607.     default:
  1608.  
  1609.         fatal("invalid mode");
  1610.     }
  1611.  
  1612.     if (this->dir_entry[entry].selected)
  1613.     this->temp[this->columns - 3] = '*';
  1614.  
  1615.     if (entry == this->current_entry)
  1616.     this->temp[0] = this->focus ?   ACTIVE_PANEL_MARKER :
  1617.                       INACTIVE_PANEL_MARKER;
  1618.  
  1619.     if (TypeSensitivity && this->dir_entry[entry].type != DIR_ENTRY &&
  1620.     !this->dir_entry[entry].selected)
  1621.     {
  1622.     /* Notify about the cursor movement just once.  */
  1623.     window_goto(this->window, entry - this->first_on_screen + 1, 1);
  1624.  
  1625.     /* Display the first character of the entry.  That character is
  1626.        either a space or the '>' character (the current file
  1627.        marker).  */
  1628.  
  1629.     brightness = this->dir_entry[entry].selected ?
  1630.              PanelSelectedFileBrightness :
  1631.              PanelNotSelectedFileBrightness;
  1632.     foreground = this->dir_entry[entry].selected ?
  1633.              PanelSelectedFile :
  1634.              PanelNotSelectedFile;
  1635.  
  1636.     if (entry == this->current_entry && this->focus == ON)
  1637.         background = PanelCurrentFile;
  1638.     else
  1639.         background = PanelBackground;
  1640.  
  1641.     tty_colors(brightness, foreground, background);
  1642.  
  1643.     window_putc(*this->temp);
  1644.  
  1645.     /* Try to match the current file name against the specified
  1646.        patterns.  Display it with the appropriate color.  */
  1647.     if (this->dir_entry[entry].fti_loaded == 0)
  1648.         load_fti(this, entry);
  1649.  
  1650.     if (this->dir_entry[entry].brightness != 2)
  1651.         if (entry == this->current_entry && this->focus == ON)
  1652.         tty_colors(brightness,
  1653.                this->dir_entry[entry].foreground,
  1654.                background);
  1655.         else
  1656.         tty_colors(this->dir_entry[entry].brightness,
  1657.                this->dir_entry[entry].foreground,
  1658.                this->dir_entry[entry].background);
  1659.  
  1660.     window_puts(this->temp + 1, len + 1);
  1661.  
  1662.     /* Display the end of the entry (the part after the file name).  */
  1663.  
  1664.     tty_colors(brightness, foreground, background);
  1665.  
  1666.     window_puts(this->temp + 1 + len + 1, this->columns - len - 2 - 2);
  1667.     }
  1668.     else
  1669.     {
  1670.     if (entry == this->current_entry && this->focus == ON)
  1671.     {
  1672.         foreground = this->dir_entry[entry].selected ?
  1673.                  PanelCurrentSelectedFile :
  1674.                  PanelCurrentNotSelectedFile;
  1675.         background = PanelCurrentFile;
  1676.     }
  1677.     else
  1678.     {
  1679.         foreground = this->dir_entry[entry].selected ?
  1680.                  PanelSelectedFile :
  1681.                  PanelNotSelectedFile;
  1682.         background = PanelBackground;
  1683.     }
  1684.  
  1685.     brightness = this->dir_entry[entry].selected ?
  1686.              PanelSelectedFileBrightness :
  1687.              PanelNotSelectedFileBrightness;
  1688.  
  1689.     tty_colors(brightness, foreground, background);
  1690.  
  1691.     window_goto(this->window, entry - this->first_on_screen + 1, 1);
  1692.     window_puts(this->temp, this->columns - 2);
  1693.     }
  1694. }
  1695.  
  1696.  
  1697. void
  1698. panel_update_frame(this)
  1699.     panel_t *this;
  1700. {
  1701.     int i;
  1702.     tty_status_t status;
  1703.  
  1704.     if (this->visible)
  1705.     return;
  1706.  
  1707.     tty_save(&status);
  1708.  
  1709.     tty_colors(OFF, PanelFrame, PanelFrame);
  1710.  
  1711.     for (i = 1; i < this->lines - 1; i++)
  1712.     {
  1713.     window_goto(this->window, i, 0);
  1714.     window_putc(' ');
  1715.     }
  1716.  
  1717.     for (i = 1; i < this->lines - 1; i++)
  1718.     {
  1719.     window_goto(this->window, i, this->columns - 1);
  1720.     window_putc(' ');
  1721.     }
  1722.  
  1723.     window_goto(this->window, 0, 0);
  1724.     window_puts("  ", 2);
  1725.     window_goto(this->window, 0, this->columns - 14 - 1 - 2);
  1726.     window_putc(' ');
  1727.     window_goto(this->window, 0, this->columns - 2);
  1728.     window_puts("  ", 2);
  1729.  
  1730.     window_goto(this->window, this->lines - 1, 0);
  1731.     window_puts("  ", 2);
  1732.     window_goto(this->window, this->lines - 1, this->columns - 2);
  1733.     window_puts("  ", 2);
  1734.  
  1735.     tty_restore(&status);
  1736. }
  1737.  
  1738.  
  1739. void
  1740. panel_update(this)
  1741.     panel_t *this;
  1742. {
  1743.     panel_update_frame(this);
  1744.     panel_update_path(this);
  1745.     panel_update_info(this);
  1746.     panel_update_size(this);
  1747.     panel_update_entries(this);
  1748. }
  1749.  
  1750.  
  1751. void
  1752. panel_resize(this, x, y, lines, columns)
  1753.     panel_t *this;
  1754.     int x, y;
  1755.     int lines, columns;
  1756. {
  1757.     this->x = x;
  1758.     this->y = y;
  1759.  
  1760.     this->lines   = lines;
  1761.     this->columns = columns;
  1762.  
  1763.     this->temp = xrealloc(this->temp, this->columns);
  1764.  
  1765.     window_resize(this->window, x, y, lines, columns);
  1766.  
  1767.     panel_set_position(this, this->current_entry);
  1768.  
  1769.     panel_update(this);
  1770. }
  1771.  
  1772.  
  1773. void
  1774. panel_set_focus(this, status)
  1775.     panel_t *this;
  1776.     int status;
  1777. {
  1778.     this->focus = status;
  1779.     panel_update_entry(this, this->current_entry);
  1780.  
  1781.     if (this->focus)
  1782.     if (chdir(this->path) == -1)
  1783.         panel_recover(this);
  1784. }
  1785.  
  1786.  
  1787. void
  1788. panel_select_all(this)
  1789.     panel_t *this;
  1790. {
  1791.     int i;
  1792.  
  1793.     this->selected_files = 0;
  1794.  
  1795.     for (i = 0; i < this->entries; i++)
  1796.     if (this->dir_entry[i].type != DIR_ENTRY)
  1797.     {
  1798.         this->dir_entry[i].selected = 1;
  1799.         this->selected_files++;
  1800.     }
  1801. }
  1802.  
  1803.  
  1804. void
  1805. panel_unselect_all(this)
  1806.     panel_t *this;
  1807. {
  1808.     int i;
  1809.  
  1810.     for (i = 0; i < this->entries; i++)
  1811.     this->dir_entry[i].selected = 0;
  1812.  
  1813.     this->selected_files = 0;
  1814. }
  1815.  
  1816.  
  1817. char *
  1818. panel_get_path(this)
  1819.     panel_t *this;
  1820. {
  1821.     return this->path;
  1822. }
  1823.  
  1824.  
  1825. int
  1826. canceled()
  1827. {
  1828.     int key;
  1829.  
  1830.     if (UserHeartAttack)
  1831.     {
  1832.     input_line_t *saved_il;
  1833.  
  1834.     UserHeartAttack = 0;
  1835.     saved_il = il_save();
  1836.     key = panel_1s_message("Abort current operation ? ", "yn",
  1837.                    IL_FREEZED | IL_BEEP);
  1838.     il_restore(saved_il);
  1839.     il_full_update();
  1840.     return (key == 'n' || key == 'N') ? 0 : 1;
  1841.     }
  1842.  
  1843.     return 0;
  1844. }
  1845.  
  1846.  
  1847. /* Check if two file names point to the same file.  It works by checking the
  1848.    devices and inodes.  */
  1849.  
  1850. int
  1851. same_file(file1, file2)
  1852.     char *file1;
  1853.     char *file2;
  1854. {
  1855.     struct stat s1;
  1856.     struct stat s2;
  1857.  
  1858.     if (xstat(file1, &s1) == 0 &&
  1859.     xstat(file2, &s2) == 0 &&
  1860.     s1.st_dev == s2.st_dev &&
  1861.     s1.st_ino == s2.st_ino)
  1862.     return 1;
  1863.  
  1864.     return 0;
  1865. }
  1866.  
  1867.  
  1868. #define WARN_OK         1
  1869. #define WARN_CANCEL     2
  1870. #define WARN_SKIP       3
  1871.  
  1872.  
  1873. int
  1874. panel_warning(this, file)
  1875.     panel_t *this;
  1876.     char *file;
  1877. {
  1878.     char c;
  1879.  
  1880.     if (this->selected_files)
  1881.     c = panel_2s_message("%s: File exists. Overwrite/Skip/All/Cancel ? ",
  1882.                  file, "osac", IL_MOVE|IL_BEEP|IL_SAVE|IL_ERROR);
  1883.     else
  1884.     c = panel_2s_message("%s: File exists. Overwrite/Cancel ? ",
  1885.                  file, "oc", IL_MOVE|IL_BEEP|IL_SAVE|IL_ERROR);
  1886.  
  1887.     switch (c)
  1888.     {
  1889.     case 'o':
  1890.         break;
  1891.  
  1892.     case 'a':
  1893.         if (this->selected_files)
  1894.         {
  1895.         this->chkdest = OFF;
  1896.         break;
  1897.         }
  1898.  
  1899.     case 's':
  1900.         if (this->selected_files)
  1901.         return WARN_SKIP;
  1902.  
  1903.     default:
  1904.         return WARN_CANCEL;
  1905.     }
  1906.  
  1907.     return 0;
  1908. }
  1909.  
  1910.  
  1911. #define COPY_BUFFER_SIZE        (32 * 1024)
  1912.  
  1913. #define SD_OK            WARN_OK
  1914. #define SD_CANCEL        WARN_CANCEL
  1915. #define SD_SKIP          WARN_SKIP
  1916. #define S_OPENERR        4
  1917. #define S_READERR        5
  1918. #define D_CREATERR       6
  1919. #define D_WRITEERR       7
  1920. #define SD_NOSPACE       8
  1921. #define SD_UNKNOWN       9
  1922. #define D_STATERR       10
  1923. #define SD_INVAL        11
  1924.  
  1925.  
  1926. char *copyerr[11] =
  1927. {
  1928.     "",
  1929.     "",
  1930.     "",
  1931.     "cannot open source file",
  1932.     "cannot read from source file",
  1933.     "cannot create destination file",
  1934.     "cannot write to destination file",
  1935.     "not enough space on device",
  1936.     "unknown error",
  1937.     "cannot stat destination file",
  1938.     "cannot copy a directory to a non-directory"
  1939. };
  1940.  
  1941.  
  1942. int
  1943. panel_copy(this, src, dest, mode, uid, gid)
  1944.     panel_t *this;
  1945.     char *src, *dest;
  1946.     mode_t mode;
  1947.     uid_t uid;
  1948.     gid_t gid;
  1949. {
  1950.     struct stat dest_statbuf;
  1951.     int shandle, dhandle, i, err;
  1952.     char *buf, *dest_file, *msg;
  1953.     size_t len, memsize, bytes_to_transfer;
  1954.  
  1955.  
  1956.     if (S_ISDIR(mode))
  1957.     {
  1958.     /* The source file is a directory.  */
  1959.     int result;
  1960.     char *temp;
  1961.  
  1962.     if (xstat(dest, &dest_statbuf) == 0)
  1963.     {
  1964.         /* The destination exists.  */
  1965.  
  1966.         /* We can't copy a directory to a non-directory.  */
  1967.         if (!S_ISDIR(dest_statbuf.st_mode))
  1968.         return SD_INVAL;
  1969.  
  1970.         /* The destination is a directory.  The directory 'dest'
  1971.            might contain a directory 'src' so we have to check
  1972.            it out.  */
  1973.         temp = xmalloc(strlen(dest) + 1 + strlen(src) + 1);
  1974.  
  1975.         sprintf(temp, "%s/%s", dest, src);
  1976.  
  1977.         if (this->chkdest && access(temp, 0) == 0)
  1978.         {
  1979.         int error;
  1980.         STATUS_RESTORE();
  1981.         error = panel_warning(this, temp);
  1982.         xfree(temp);
  1983.         if (error)
  1984.             return error;
  1985.         }
  1986.         else
  1987.         xfree(temp);
  1988.     }
  1989.  
  1990.     /* The 'dest' directory contains no 'src' directory or we have
  1991.        the permission to overwrite it, so we may proceed.  */
  1992.     temp = xmalloc(32 + strlen(src) + strlen(dest) + 1);
  1993.  
  1994.     dest_file = xbasename(dest);
  1995.  
  1996.     if (*dest_file == '\0')
  1997.         return D_CREATERR;
  1998.  
  1999.     msg = xmalloc(32 + strlen(src));
  2000.     sprintf(msg, "(COPY) [cp -r] %s", src);
  2001.     status(msg, 0, 0, 0, MSG_STATUS, 0);
  2002.     xfree(msg);
  2003.  
  2004.     sprintf(temp, "cp -r %s %s", src, dest);
  2005.     result = start(temp, 1) == 0;
  2006.     xfree(temp);
  2007.  
  2008.     if (!result)
  2009.         display_errors("cp");
  2010.  
  2011.     return result ? SD_OK : SD_UNKNOWN;
  2012.     }
  2013.  
  2014.     /* The source is a regular file.  */
  2015.     len  = strlen(dest);
  2016.     dest = xstrdup(dest);
  2017.  
  2018.     if (xstat(dest, &dest_statbuf) == 0 && S_ISDIR(dest_statbuf.st_mode))
  2019.     {
  2020.     /* The destination is a directory.  */
  2021.     dest = xrealloc(dest, len + 1 + strlen(src) + 1);
  2022.     strcat(dest, "/");
  2023.     strcat(dest, src);
  2024.     }
  2025.  
  2026.     dest_file = xbasename(dest);
  2027.  
  2028.     if (*dest_file == '\0')
  2029.     {
  2030.     xfree(dest);
  2031.     return D_CREATERR;
  2032.     }
  2033.  
  2034.     if (this->chkdest && (access(dest, 0) == 0))
  2035.     {
  2036.     int error;
  2037.     STATUS_RESTORE();
  2038.     error = panel_warning(this, dest_file);
  2039.     if (error)
  2040.         return error;
  2041.     }
  2042.  
  2043.     msg = xmalloc(32 + strlen(src));
  2044.  
  2045.     if (S_ISREG(mode))
  2046.     sprintf(msg, "(COPY) [  0%%] %s", src);
  2047.     else
  2048.     sprintf(msg, "(COPY) [0 bytes] %s", src);
  2049.  
  2050.     status(msg, 0, 0, 0, MSG_STATUS, 0);
  2051.     xfree(msg);
  2052.  
  2053.     if ((shandle = open(src, O_RDONLY)) == -1)
  2054.     return S_OPENERR;
  2055.  
  2056. #ifdef HAVE_LINUX
  2057.     /* Ignore the executable bits when copying from a MSDOG file system.  */
  2058.     if (this->msdosfs)
  2059.     mode &= ~0111;
  2060. #endif  /* HAVE_LINUX */
  2061.  
  2062.     if ((dhandle = creat(dest, mode)) == -1)
  2063.     {
  2064.     close(shandle);
  2065.     xfree(dest);
  2066.     return D_CREATERR;
  2067.     }
  2068.  
  2069.     memsize = min(len = get_file_length(shandle), COPY_BUFFER_SIZE);
  2070.  
  2071.     if (S_ISBLK(mode) || S_ISCHR(mode))
  2072.     {
  2073.     len = INT_MAX;
  2074.     memsize = COPY_BUFFER_SIZE;
  2075.     }
  2076.  
  2077.     if (len == 0)
  2078.     {
  2079.     close2(shandle, dhandle);
  2080.     xfree(dest);
  2081.     return SD_OK;
  2082.     }
  2083.  
  2084.     buf = xmalloc(memsize);
  2085.  
  2086.     for (i = 0; i < len; i += COPY_BUFFER_SIZE)
  2087.     {
  2088.     bytes_to_transfer = min(len - i, memsize);
  2089.  
  2090.     if (canceled())
  2091.     {
  2092.         close2(shandle, dhandle);
  2093.         unlink(dest);
  2094.         xfree2(buf, dest);
  2095.         return SD_CANCEL;
  2096.     }
  2097.  
  2098.     err = xread(shandle, buf, bytes_to_transfer);
  2099.  
  2100.     if (err != bytes_to_transfer)
  2101.         if (err >= 0)
  2102.         {
  2103.         if (err)
  2104.             bytes_to_transfer = err;
  2105.         else
  2106.         {
  2107.             close2(shandle, dhandle);
  2108.             xfree2(buf, dest);
  2109.             return SD_OK;
  2110.         }
  2111.         }
  2112.         else
  2113.         {
  2114.         close2(shandle, dhandle);
  2115.         unlink(dest);
  2116.         xfree2(buf, dest);
  2117.         return S_READERR;
  2118.         }
  2119.  
  2120.     if (canceled())
  2121.     {
  2122.         close2(shandle, dhandle);
  2123.         unlink(dest);
  2124.         xfree2(buf, dest);
  2125.         return SD_CANCEL;
  2126.     }
  2127.  
  2128.     err = xwrite(dhandle, buf, bytes_to_transfer);
  2129.  
  2130.     if (err != bytes_to_transfer)
  2131.         if (err >= 0)
  2132.         {
  2133.         close2(shandle, dhandle);
  2134.         unlink(dest);
  2135.         xfree2(buf, dest);
  2136.         return SD_NOSPACE;
  2137.         }
  2138.         else
  2139.         {
  2140.         close2(shandle, dhandle);
  2141.         unlink(dest);
  2142.         xfree2(buf, dest);
  2143.         return (errno == ENOSPC) ? SD_NOSPACE : D_WRITEERR;
  2144.         }
  2145.  
  2146.     if (i + bytes_to_transfer <= len)
  2147.     {
  2148.         msg = xmalloc(32 + strlen(src));
  2149.  
  2150.         if (S_ISREG(mode))
  2151.         sprintf(msg, "(COPY) [%3d%%] %s",
  2152.             ((i + bytes_to_transfer) * 100) / len, src);
  2153.         else
  2154.         sprintf(msg, "(COPY) [%ld bytes] %s",
  2155.             (long)(i + bytes_to_transfer), src);
  2156.         status(msg, 0, 0, 0, MSG_STATUS, 0);
  2157.         xfree(msg);
  2158.     }
  2159.     }
  2160.  
  2161.     if (getuid() == 0)
  2162.     chown(dest, uid, gid);
  2163.  
  2164.     close2(shandle, dhandle);
  2165.     xfree2(buf, dest);
  2166.  
  2167.     return SD_OK;
  2168. }
  2169.  
  2170.  
  2171. #define FT_OK           WARN_OK
  2172. #define FT_CANCEL       WARN_CANCEL
  2173. #define FT_SKIP         WARN_SKIP
  2174. #define T_CREATERR      4
  2175. #define F_DELETERR      5
  2176. #define F_STATERR       6
  2177. #define T_STATERR       7
  2178. #define FT_UNKNOWN      8
  2179. #define FT_INVAL        9
  2180.  
  2181.  
  2182. char *moveerr[9] =
  2183. {
  2184.     "",
  2185.     "",
  2186.     "",
  2187.     "cannot create destination file",
  2188.     "cannot remove source file",
  2189.     "cannot stat source file",
  2190.     "cannot stat destination directory",
  2191.     "unknown error",
  2192.     "cannot copy a directory to a non-directory"
  2193. };
  2194.  
  2195.  
  2196. int
  2197. panel_move(this, from, to, mode)
  2198.     panel_t *this;
  2199.     char *from, *to;
  2200.     mode_t mode;
  2201. {
  2202.     int err;
  2203.     size_t len;
  2204.     struct stat to_statbuf;
  2205.     struct stat from_statbuf;
  2206.     char *to_file, *msg;
  2207.  
  2208.  
  2209.     if (S_ISDIR(mode))
  2210.     {
  2211.     /* The source file is a directory.  */
  2212.     int result;
  2213.     char *temp;
  2214.  
  2215.     if (xstat(to, &to_statbuf) == 0)
  2216.     {
  2217.         /* The destination exists.  */
  2218.  
  2219.         /* We can't move a directory to a non-directory.  */
  2220.         if (!S_ISDIR(to_statbuf.st_mode))
  2221.         return FT_INVAL;
  2222.  
  2223.         /* The destination is a directory.  The directory 'to'
  2224.            might contain a directory 'from' so we have to check
  2225.            it out.  */
  2226.         temp = xmalloc(strlen(to) + 1 + strlen(from) + 1);
  2227.  
  2228.         sprintf(temp, "%s/%s", to, from);
  2229.  
  2230.         if (this->chkdest && access(temp, 0) == 0)
  2231.         {
  2232.         int error;
  2233.         STATUS_RESTORE();
  2234.         error = panel_warning(this, temp);
  2235.         xfree(temp);
  2236.  
  2237.         if (error)
  2238.             return error;
  2239.         }
  2240.         else
  2241.         xfree(temp);
  2242.     }
  2243.  
  2244.     /* The 'to' directory contains no 'from' directory or we have
  2245.        the permission to overwrite it, so we may proceed.  */
  2246.     temp = xmalloc(32 + strlen(from) + strlen(to) + 1);
  2247.  
  2248.     to_file = xbasename(to);
  2249.  
  2250.     if (*to_file == '\0')
  2251.         return T_CREATERR;
  2252.  
  2253.     msg = xmalloc(32 + strlen(from));
  2254.     sprintf(msg, "(MOVE) [mv -f] %s", from);
  2255.     status(msg, 0, 0, 0, MSG_STATUS, 0);
  2256.     xfree(msg);
  2257.  
  2258.     sprintf(temp, "mv -f %s %s", from, to);
  2259.     result = start(temp, 1) == 0;
  2260.     xfree(temp);
  2261.  
  2262.     if (!result)
  2263.         display_errors("mv");
  2264.  
  2265.     return result ? FT_OK : FT_UNKNOWN;
  2266.     }
  2267.  
  2268.     /* The source is not a directory.  */
  2269.     len = strlen(to);
  2270.     to  = xstrdup(to);
  2271.  
  2272.     if (xstat(to, &to_statbuf) == 0 && S_ISDIR(to_statbuf.st_mode))
  2273.     {
  2274.     /* The destination is a directory.  */
  2275.     to = xrealloc(to, len + 1 + strlen(from) + 1);
  2276.     strcat(to, "/");
  2277.     strcat(to, from);
  2278.     }
  2279.  
  2280.     to_file = xbasename(to);
  2281.  
  2282.     if (*to_file == '\0')
  2283.     {
  2284.     xfree(to);
  2285.     return T_CREATERR;
  2286.     }
  2287.  
  2288.     if (to_file == to)
  2289.     {
  2290.     /* 'to' is relative to the current directory.  We have to add './'
  2291.        to its beginning in order to be able to stat the destination
  2292.        directory later.  */
  2293.  
  2294.     char *temp = xmalloc(2 + (len = strlen(to)) + 1);
  2295.  
  2296.     temp[0] = '.';
  2297.     temp[1] = '/';
  2298.     memcpy(temp + 2, to, len + 1);
  2299.     xfree(to);
  2300.     to = temp;
  2301.  
  2302.     /* Update 'to_file'.  */
  2303.     to_file = to + 2;
  2304.     }
  2305.  
  2306.     if (this->chkdest && (access(to, 0) == 0))
  2307.     {
  2308.     int error;
  2309.     STATUS_RESTORE();
  2310.     error = panel_warning(this, to_file);
  2311.  
  2312.     if (error)
  2313.         return error;
  2314.     }
  2315.  
  2316.     msg = xmalloc(32 + strlen(from));
  2317.     sprintf(msg, "(MOVE) %s", from);
  2318.     status(msg, 0, 0, 0, MSG_STATUS, 0);
  2319.     xfree(msg);
  2320.  
  2321.     if (xstat(from, &from_statbuf) == -1)
  2322.     return F_STATERR;
  2323.  
  2324.     if (xstat(to, &to_statbuf) == -1)
  2325.     {
  2326.     /* This is very very ugly ... :-(.  We need to stat the destination
  2327.         directory in order to find out if we can move the file.  If we
  2328.         can't, we have to copy it.  */
  2329.     char c = *(to_file - 1);
  2330.  
  2331.     *(to_file - 1) = 0;
  2332.     err = (*to) ? xstat(to, &to_statbuf) : 0;
  2333.     *(to_file - 1) = c;
  2334.  
  2335.     if (err == -1)
  2336.         return T_STATERR;
  2337.     }
  2338.  
  2339.     if (to_statbuf.st_dev != from_statbuf.st_dev
  2340. #ifdef HAVE_LINUX
  2341.     || this->msdosfs
  2342. #endif /* HAVE_LINUX */
  2343.     )
  2344.     {
  2345.     err = panel_copy(this, from, to,
  2346.              from_statbuf.st_mode,
  2347.              from_statbuf.st_uid,
  2348.              from_statbuf.st_gid);
  2349.  
  2350.     if (err == SD_OK)
  2351.         goto remove_from;
  2352.  
  2353.     if (err == SD_CANCEL)
  2354.         return FT_CANCEL;
  2355.  
  2356.     if (err == SD_SKIP)
  2357.         return FT_SKIP;
  2358.  
  2359.     panel_3s_message("%s: Copy failed, %s.", from, copyerr[err-1], NULL,
  2360.              IL_MOVE | IL_BEEP | IL_ERROR);
  2361.  
  2362.     return FT_OK;
  2363.     }
  2364.  
  2365.     if (S_ISREG(to_statbuf.st_mode) || S_ISDIR(to_statbuf.st_mode))
  2366.     {
  2367.     unlink(to);
  2368.     if (link(from, to) == -1)
  2369.         return T_CREATERR;
  2370.     }
  2371.  
  2372.   remove_from:
  2373.  
  2374.     if (unlink(from) == -1)
  2375.     return F_DELETERR;
  2376.  
  2377.     return FT_OK;
  2378. }
  2379.  
  2380.  
  2381. int
  2382. panel_get_index(this, str)
  2383.     panel_t *this;
  2384.     char *str;
  2385. {
  2386.     int i;
  2387.     size_t len = strlen(str);
  2388.     char *temp = xmalloc(len + 1);
  2389.  
  2390.  
  2391.     strncpy(temp, str, len = min(len, this->maxname));
  2392.     temp[len] = 0;
  2393.  
  2394.     for (i = 0;
  2395.      i < this->entries && strcmp(temp, this->dir_entry[i].name);
  2396.      i++);
  2397.  
  2398.     if (i == this->entries)
  2399.     {
  2400.     for (i = 0;
  2401.          i < this->entries && strcasecmp(temp, this->dir_entry[i].name);
  2402.          i++);
  2403.  
  2404.     if (i == this->entries)
  2405.     {
  2406.         xfree(temp);
  2407.         return 0;
  2408.     }
  2409.     }
  2410.  
  2411.     xfree(temp);
  2412.     return i;
  2413. }
  2414.  
  2415.  
  2416. int
  2417. panel_act_ENTER(this, link)
  2418.     panel_t *this, *link;
  2419. {
  2420.     size_t len;
  2421.     int i, back, done = 0;
  2422.     char *old_path, *cmd, *ptr;
  2423.     char *name = this->dir_entry[this->current_entry].name;
  2424.  
  2425.     switch (this->dir_entry[this->current_entry].type)
  2426.     {
  2427.     case DIR_ENTRY:
  2428.  
  2429.         if (strcmp(name, "..") == 0 && strcmp(this->path, "/") == 0)
  2430.         break;
  2431.  
  2432.         back = strcmp(name, "..") ? 0 : 1;
  2433.  
  2434.         old_path = xmalloc((len = this->pathlen) + 1);
  2435.         strcpy(old_path, this->path);
  2436.  
  2437.         if (!panel_read_directory(this, name, ON))
  2438.         {
  2439.         if (back)
  2440.             panel_recover(this);
  2441.         else
  2442.             panel_2s_message("%s/: Permission denied.", name, NULL,
  2443.                      IL_FREEZED | IL_BEEP |
  2444.                      IL_SAVE    | IL_ERROR);
  2445.         break;
  2446.         }
  2447.  
  2448.         if (back)
  2449.         {
  2450.         ptr = strrchr(old_path, '/');
  2451.  
  2452.         if (ptr == NULL)
  2453.             panel_recover(this);
  2454.  
  2455.         ptr++;
  2456.  
  2457.         for (i = 0;
  2458.              i < this->entries && strcmp(this->dir_entry[i].name, ptr);
  2459.              i++);
  2460.  
  2461.         if (i == this->entries)
  2462.             i = 0;
  2463.  
  2464.         this->current_entry = i;
  2465.         this->first_on_screen = get_centered_fos(this);
  2466.         }
  2467.         else
  2468.         this->current_entry = this->first_on_screen = 0;
  2469.  
  2470.         xfree(old_path);
  2471.  
  2472.         panel_update_path(this);
  2473.         panel_update_entries(this);
  2474.         panel_update_size(this);
  2475.  
  2476.         if (strcmp(this->path, link->path) == 0)
  2477.         panel_action(link, act_REFRESH, this, (void *)-1, 1);
  2478.  
  2479.         panel_update_size(link);
  2480.  
  2481.         done = 1;
  2482.         break;
  2483.  
  2484.     case FILE_ENTRY:
  2485.  
  2486.         if (this->dir_entry[this->current_entry].executable)
  2487.         {
  2488.         /* An ugly hack.  */
  2489.         extern int wait_msg;
  2490.  
  2491.         len = 32 + strlen(name) + 1;
  2492.  
  2493.         cmd = xmalloc(len);
  2494.         sprintf(cmd, "./\"%s\"", name);
  2495.         start(cmd, 0);
  2496.         wait_msg = 1;
  2497.         xfree(cmd);
  2498.  
  2499.         tty_touch();
  2500.  
  2501.         panel_no_optimizations(this);
  2502.         panel_no_optimizations(link);
  2503.  
  2504.         il_insert_text(name);
  2505.  
  2506.         done = -1;
  2507.         }
  2508.  
  2509.         break;
  2510.     }
  2511.  
  2512.     return done;
  2513. }
  2514.  
  2515.  
  2516. void
  2517. panel_act_COPY(this, link)
  2518.     panel_t *this, *link;
  2519. {
  2520.     size_t len;
  2521.     int err, entry;
  2522.     char *file, *dir = NULL, *msg, *input = NULL, *tmp_input;
  2523.  
  2524.  
  2525.     this->chkdest = ON;
  2526.  
  2527.     if (this->selected_files == 0)
  2528.     {
  2529.     char *name = this->dir_entry[this->current_entry].name;
  2530.  
  2531.     if (this->current_entry == 0 && !rootdir())
  2532.         return;
  2533.  
  2534.     msg = xmalloc(16 + strlen(name) + 1);
  2535.     sprintf(msg, "Copy %s to: ", cutname(name, 0, 0));
  2536.  
  2537.     len  = 1 + strlen(name) + 1;
  2538.     file = xmalloc(strlen(link->path) + len);
  2539.  
  2540.     sprintf(file, "%s/%s", link->path, name);
  2541.  
  2542.     if (!il_read_line(msg, &input, file, copy_history))
  2543.     {
  2544.         xfree(msg);
  2545.         return;
  2546.     }
  2547.  
  2548.     xfree(msg);
  2549.  
  2550.     if (S_ISDIR(this->dir_entry[this->current_entry].mode))
  2551.         il_message(PANEL_COPY_DIR_MSG);
  2552.     else
  2553.         il_message(PANEL_COPY_FILE_MSG);
  2554.  
  2555.     tmp_input = tilde_expand(input);
  2556.     xfree(input);
  2557.     input = tmp_input;
  2558.  
  2559.     err = same_file(name, input);
  2560.     xfree(file);
  2561.  
  2562.     if (err)
  2563.     {
  2564.         panel_3s_message("%s and %s point to the same file.", name, input,
  2565.                  NULL, IL_MOVE | IL_BEEP | IL_SAVE | IL_ERROR);
  2566.         xfree(input);
  2567.         return;
  2568.     }
  2569.  
  2570.     err = panel_copy(this, name, input,
  2571.              this->dir_entry[this->current_entry].mode,
  2572.              this->dir_entry[this->current_entry].uid,
  2573.              this->dir_entry[this->current_entry].gid);
  2574.  
  2575.     xfree(input);
  2576.  
  2577.     if (err != SD_OK && err != SD_CANCEL)
  2578.         panel_3s_message("%s: Copy failed, %s.", name, copyerr[err-1],
  2579.                  NULL, IL_MOVE | IL_BEEP | IL_SAVE | IL_ERROR);
  2580.  
  2581.     STATUS_RESTORE();
  2582.     panel_update_size(this);
  2583.     panel_update_size(link);
  2584.     }
  2585.     else
  2586.     {
  2587.     if (!il_read_line("Copy selected file(s) to: ", &dir,
  2588.               link->path, copy_history))
  2589.         return;
  2590.  
  2591.     if (same_file(this->path, dir))
  2592.     {
  2593.         panel_1s_message(nice_try, NULL, IL_FREEZED | IL_BEEP | IL_ERROR);
  2594.         return;
  2595.     }
  2596.  
  2597.     dir = xrealloc(dir, (len = strlen(dir) + 1) + 1);
  2598.     dir[len-1] = '/';
  2599.     dir[len  ] = '\0';
  2600.  
  2601.     panel_init_iterator(this);
  2602.  
  2603.     while ((entry = panel_get_next(this)) != -1)
  2604.     {
  2605.         char *name  = this->dir_entry[entry].name;
  2606.         mode_t mode = this->dir_entry[entry].mode;
  2607.         uid_t uid   = this->dir_entry[entry].uid;
  2608.         gid_t gid   = this->dir_entry[entry].gid;
  2609.  
  2610.         dir = xrealloc(dir, len + strlen(name) + 1);
  2611.         strcpy(dir + len, name);
  2612.  
  2613.         if (canceled())
  2614.         break;
  2615.  
  2616.         il_message(PANEL_COPY_FILES_MSG);
  2617.  
  2618.         err = panel_copy(this, name, dir, mode, uid, gid);
  2619.  
  2620.         if (err != SD_OK)
  2621.         {
  2622.         if (err == SD_CANCEL)
  2623.             break;
  2624.  
  2625.         if (err == SD_SKIP)
  2626.             continue;
  2627.  
  2628.         if (panel_3s_message("%s: Copy failed, %s.",
  2629.                      name, copyerr[err-1], NULL,
  2630.                      IL_MOVE | IL_BEEP | IL_ERROR) == 0)
  2631.             break;
  2632.         }
  2633.         else
  2634.         this->dir_entry[entry].selected = 0;
  2635.  
  2636.         panel_update_size(this);
  2637.         panel_update_size(link);
  2638.     }
  2639.  
  2640.     if (dir)
  2641.         xfree(dir);
  2642.  
  2643.     STATUS_RESTORE();
  2644.     }
  2645.  
  2646.     if (!panel_read_directory(link, link->path, ON))
  2647.     panel_recover(link);
  2648.     else
  2649.     {
  2650.     panel_update_entries(link);
  2651.     panel_update_info(link);
  2652.     }
  2653.  
  2654.     if (!panel_read_directory(this, this->path, ON))
  2655.     panel_recover(this);
  2656.     else
  2657.     {
  2658.     panel_update_entries(this);
  2659.     panel_update_info(this);
  2660.     }
  2661. }
  2662.  
  2663.  
  2664. void
  2665. panel_act_DELETE(this, link)
  2666.     panel_t *this, *link;
  2667. {
  2668.     char *temp;
  2669.     char *msg;
  2670.     int keep_asking = 1;
  2671.     int i, entry, answer = 0, result;
  2672.  
  2673.  
  2674.     if (this->selected_files == 0 && (this->current_entry == 0 && !rootdir()))
  2675.     return;
  2676.  
  2677.     if (panel_1s_message("Delete selected entries ? ","yn",IL_FREEZED) != 'y')
  2678.     return;
  2679.  
  2680.     for (i = 0; i < this->entries; i++)
  2681.     if (this->dir_entry[i].selected)
  2682.         break;
  2683.  
  2684.     panel_init_iterator(this);
  2685.  
  2686.     while ((entry = panel_get_next(this)) != -1)
  2687.     {
  2688.     char *name = this->dir_entry[entry].name;
  2689.  
  2690.     msg = xmalloc(32 + strlen(name));
  2691.     sprintf(msg, "(DELETE) %s", name);
  2692.     status(msg, 0, 0, 0, MSG_ERROR, 0);
  2693.     xfree(msg);
  2694.  
  2695.     if (canceled())
  2696.         break;
  2697.  
  2698.     if (keep_asking)
  2699.         answer = panel_2s_message("Delete %s ? (Yes/Skip/All/Cancel) ",
  2700.                       name, "ysac", IL_MOVE);
  2701.  
  2702.     il_message(PANEL_DELETE_FILES_MSG);
  2703.  
  2704.     if (answer == 'a')
  2705.         keep_asking = 0;
  2706.     else
  2707.         if (answer == 's')
  2708.         continue;
  2709.         else
  2710.         if (answer == 'c')
  2711.             break;
  2712.         else
  2713.             if (answer != 'y')
  2714.             break;
  2715.  
  2716.     if (this->dir_entry[entry].type == DIR_ENTRY)
  2717.     {
  2718.         il_message(PANEL_DELETE_DIR_MSG);
  2719.         result = (rmdir(name) == 0);
  2720.  
  2721.         if (!result)
  2722.         {
  2723.         if (panel_2s_message(
  2724.             "%s/: directory might contain files. Delete ? ",
  2725.             name, "yn", IL_MOVE | IL_SAVE) == 'y')
  2726.         {
  2727.             temp = xmalloc(32 + strlen(name) + 1);
  2728.             sprintf(temp, "rm -r -f %s", name);
  2729.             result = start(temp, 1) == 0;
  2730.             xfree(temp);
  2731.  
  2732.             if (!result)
  2733.             display_errors("rm");
  2734.         }
  2735.         }
  2736.     }
  2737.     else
  2738.         result = unlink(name) == 0;
  2739.  
  2740.     if (!result)
  2741.     {
  2742.         if (panel_2s_message("%s: Deletion failed.  Continue ? ",
  2743.                  name, "yn",
  2744.                  IL_MOVE | IL_BEEP | IL_ERROR) != 'y')
  2745.         break;
  2746.     }
  2747.     else
  2748.         this->dir_entry[entry].selected = 0;
  2749.     }
  2750.  
  2751.     if (i != this->entries)
  2752.     this->current_entry = i;
  2753.  
  2754.     panel_update_size(this);
  2755.     panel_update_size(link);
  2756.     STATUS_RESTORE();
  2757.  
  2758.     if (!panel_read_directory(this, this->path, ON))
  2759.     panel_recover(this);
  2760.     else
  2761.     {
  2762.     this->current_entry = min(this->current_entry, this->entries - 1);
  2763.     this->first_on_screen = get_centered_fos(this);
  2764.     panel_update_entries(this);
  2765.     panel_update_info(this);
  2766.     }
  2767.  
  2768.     if (strcmp(this->path, link->path) == 0)
  2769.     {
  2770.     if (!panel_read_directory(link, link->path, ON))
  2771.         panel_recover(link);
  2772.     else
  2773.     {
  2774.         link->current_entry = min(link->current_entry, link->entries - 1);
  2775.         link->first_on_screen = get_centered_fos(link);
  2776.         panel_update_entries(link);
  2777.         panel_update_info(link);
  2778.     }
  2779.     }
  2780. }
  2781.  
  2782.  
  2783. void
  2784. panel_act_MKDIR(this, link)
  2785.     panel_t *this, *link;
  2786. {
  2787.     char *input = NULL, *tmp_input;
  2788.  
  2789.     if (!il_read_line("New directory name: ", &input, NULL, mkdir_history))
  2790.     return;
  2791.  
  2792.     if (input[0] == '\0')
  2793.     {
  2794.     xfree(input);
  2795.     return;
  2796.     }
  2797.  
  2798.     tmp_input = tilde_expand(input);
  2799.     xfree(input);
  2800.     input = tmp_input;
  2801.  
  2802.     if (mkdir(input, S_IFDIR | S_IRWXU | S_IRWXG) == -1)
  2803.     {
  2804.     panel_2s_message("%s/: Permission denied.", input,
  2805.              NULL, IL_FREEZED | IL_BEEP | IL_SAVE | IL_ERROR);
  2806.     xfree(input);
  2807.     return;
  2808.     }
  2809.  
  2810.     if (!panel_read_directory(this, this->path, ON))
  2811.     panel_recover(this);
  2812.     else
  2813.     {
  2814.     this->current_entry = panel_get_index(this, input);
  2815.     this->first_on_screen = get_centered_fos(this);
  2816.     panel_update_entries(this);
  2817.     panel_update_info(this);
  2818.     panel_update_size(this);
  2819.     }
  2820.  
  2821.     if (strcmp(this->path, link->path) == 0)
  2822.     if (!panel_read_directory(link, link->path, ON))
  2823.         panel_recover(link);
  2824.     else
  2825.     {
  2826.         panel_update_entries(link);
  2827.         panel_update_info(link);
  2828.     }
  2829.  
  2830.     panel_update_size(link);
  2831.     xfree(input);
  2832. }
  2833.  
  2834.  
  2835. void
  2836. panel_act_MOVE(this, link)
  2837.     panel_t *this, *link;
  2838. {
  2839.     size_t len;
  2840.     int i, entry, err;
  2841.     char *file, *dir = NULL, *msg, *input = NULL, *tmp_input;
  2842.  
  2843.  
  2844.     this->chkdest = ON;
  2845.  
  2846.     if (this->selected_files == 0)
  2847.     {
  2848.     char *name = this->dir_entry[this->current_entry].name;
  2849.  
  2850.     if (this->current_entry == 0 && !rootdir())
  2851.         return;
  2852.  
  2853.     msg = xmalloc(16 + strlen(name) + 1);
  2854.     sprintf(msg, "Move %s to: ", cutname(name, 0, 0));
  2855.  
  2856.     len  = 1 + strlen(name) + 1;
  2857.     file = xmalloc(strlen(link->path) + len);
  2858.  
  2859.     sprintf(file, "%s/%s", link->path, name);
  2860.  
  2861.     if (!il_read_line(msg, &input, file, move_history))
  2862.     {
  2863.         xfree(msg);
  2864.         return;
  2865.     }
  2866.  
  2867.     xfree(msg);
  2868.  
  2869.     if (S_ISDIR(this->dir_entry[this->current_entry].mode))
  2870.         il_message(PANEL_MOVE_DIR_MSG);
  2871.     else
  2872.         il_message(PANEL_MOVE_FILE_MSG);
  2873.  
  2874.     tmp_input = tilde_expand(input);
  2875.     xfree(input);
  2876.     input = tmp_input;
  2877.  
  2878.     err = same_file(name, input);
  2879.     xfree(file);
  2880.  
  2881.     if (err)
  2882.     {
  2883.         panel_3s_message("%s and %s point to the same file.", name, input,
  2884.                  NULL, IL_MOVE | IL_BEEP | IL_SAVE | IL_ERROR);
  2885.         xfree(input);
  2886.         return;
  2887.     }
  2888.  
  2889.     err = panel_move(this, name, input,
  2890.              this->dir_entry[this->current_entry].mode);
  2891.  
  2892.     if (err != FT_OK)
  2893.     {
  2894.         if (err == FT_CANCEL)
  2895.         {
  2896.         xfree(input);
  2897.         STATUS_RESTORE();
  2898.         return;
  2899.         }
  2900.  
  2901.         panel_3s_message("%s: Move failed, %s.", name, moveerr[err-1],
  2902.                  NULL, IL_MOVE | IL_BEEP | IL_ERROR);
  2903.     }
  2904.  
  2905.     xfree(input);
  2906.     STATUS_RESTORE();
  2907.     panel_update_size(this);
  2908.     panel_update_size(link);
  2909.     }
  2910.     else
  2911.     {
  2912.     if (!il_read_line("Move selected file(s) to: ", &dir,
  2913.               link->path, move_history))
  2914.         return;
  2915.  
  2916.     if (same_file(this->path, dir))
  2917.     {
  2918.         panel_1s_message(nice_try, NULL, IL_FREEZED | IL_BEEP | IL_ERROR);
  2919.         return;
  2920.     }
  2921.  
  2922.     dir = xrealloc(dir, (len = strlen(dir) + 1) + 1);
  2923.     dir[len-1] = '/';
  2924.     dir[len  ] = '\0';
  2925.  
  2926.     for (i = 0; i < this->entries; i++)
  2927.         if (this->dir_entry[i].selected)
  2928.         break;
  2929.  
  2930.     panel_init_iterator(this);
  2931.  
  2932.     while ((entry = panel_get_next(this)) != -1)
  2933.     {
  2934.         char *name = this->dir_entry[entry].name;
  2935.  
  2936.         dir = xrealloc(dir, len + strlen(name) + 1);
  2937.         strcpy(dir + len, name);
  2938.  
  2939.         if (canceled())
  2940.         break;
  2941.  
  2942.         il_message(PANEL_MOVE_FILES_MSG);
  2943.  
  2944.         err = panel_move(this, name, dir, this->dir_entry[entry].mode);
  2945.  
  2946.         if (err != FT_OK)
  2947.         {
  2948.         if (err == FT_CANCEL)
  2949.             break;
  2950.  
  2951.         if (err == FT_SKIP)
  2952.             continue;
  2953.  
  2954.         if (panel_3s_message("%s: Move failed, %s.", name,
  2955.                      moveerr[err-1], NULL,
  2956.                      IL_MOVE | IL_BEEP | IL_ERROR) == 0)
  2957.             break;
  2958.         }
  2959.         else
  2960.         this->dir_entry[entry].selected = 0;
  2961.     }
  2962.  
  2963.     if (dir)
  2964.         xfree(dir);
  2965.  
  2966.     if (i != this->entries)
  2967.         this->current_entry = i;
  2968.  
  2969.     STATUS_RESTORE();
  2970.     }
  2971.  
  2972.     if (!panel_read_directory(link, link->path, ON))
  2973.     panel_recover(link);
  2974.     else
  2975.     {
  2976.     link->current_entry = min(link->current_entry, link->entries - 1);
  2977.     link->first_on_screen = get_centered_fos(link);
  2978.     panel_update_entries(link);
  2979.     panel_update_info(link);
  2980.     panel_update_size(link);
  2981.     }
  2982.  
  2983.     if (!panel_read_directory(this, this->path, ON))
  2984.     panel_recover(this);
  2985.     else
  2986.     {
  2987.     this->current_entry = min(this->current_entry, this->entries - 1);
  2988.  
  2989.     this->first_on_screen = get_centered_fos(this);
  2990.     panel_update_entries(this);
  2991.     panel_update_info(this);
  2992.     panel_update_size(this);
  2993.     }
  2994. }
  2995.  
  2996.  
  2997. void
  2998. panel_act_CHDIR(this, link, new_dir)
  2999.     panel_t *this, *link;
  3000.     char *new_dir;
  3001. {
  3002.     this->first_on_screen = this->current_entry = 0;
  3003.  
  3004.     if (new_dir[0] == '/')
  3005.     {
  3006.     this->pathlen = strlen(new_dir);
  3007.     this->path = xrealloc(this->path, this->pathlen + 1);
  3008.     strcpy(this->path, new_dir);
  3009.     }
  3010.     else
  3011.     {
  3012.     this->pathlen += 1 + strlen(new_dir);
  3013.     this->path = xrealloc(this->path, this->pathlen + 1);
  3014.     strcat(this->path, "/");
  3015.     strcat(this->path, new_dir);
  3016.     clear_path(this->path);
  3017.     }
  3018.  
  3019.     panel_action(this, act_REFRESH, NULL, NULL, 1);
  3020.  
  3021.     if (strcmp(this->path, link->path) == 0)
  3022.     panel_action(link, act_REFRESH, NULL, (void *)-1, 1);
  3023. }
  3024.  
  3025.  
  3026. void
  3027. panel_act_REFRESH(this, aux_info)
  3028.     panel_t *this;
  3029.     void *aux_info;
  3030. {
  3031.     int flag, verify;
  3032.     char *old_entry;
  3033.  
  3034.     if (this->dir_entry && this->dir_entry[this->current_entry].name)
  3035.     {
  3036.     old_entry = xstrdup(this->dir_entry[this->current_entry].name);
  3037.     flag = 1;
  3038.     }
  3039.     else
  3040.     old_entry = "", flag = 0;
  3041.  
  3042.     verify = aux_info == (void *)-1;
  3043.  
  3044.     if (!panel_read_directory(this, this->path, verify))
  3045.     panel_recover(this);
  3046.     else
  3047.     if (verify)
  3048.     {
  3049.         this->current_entry = min(panel_get_index(this, old_entry),
  3050.                       this->entries - 1);
  3051.         this->first_on_screen = get_centered_fos(this);
  3052.     }
  3053.     else
  3054.         this->current_entry = this->first_on_screen = 0;
  3055.  
  3056.     if (flag)
  3057.     xfree(old_entry);
  3058.  
  3059.     panel_update(this);
  3060. }
  3061.  
  3062.  
  3063. int
  3064. panel_action(this, action, link, aux_info, repeat_count)
  3065.     panel_t *this;
  3066.     int action;
  3067.     panel_t *link;
  3068.     void *aux_info;
  3069.     int repeat_count;
  3070. {
  3071.     size_t len;
  3072.     isearch_aux_t *iai;
  3073.     int i, done = 0, result;
  3074.     int need_update, need_update_all, old_current_entry;
  3075.  
  3076.  
  3077.     switch (action)
  3078.     {
  3079.     case act_ENTER:
  3080.  
  3081.         done = panel_act_ENTER(this, link);
  3082.         break;
  3083.  
  3084.     case act_COPY:
  3085.  
  3086.         panel_act_COPY(this, link);
  3087.         break;
  3088.  
  3089.     case act_DELETE:
  3090.  
  3091.         panel_act_DELETE(this, link);
  3092.         break;
  3093.  
  3094.     case act_SELECT:
  3095.  
  3096.         /* In the root directory there is no '..' entry, so the first
  3097.            entry can be selected.  Avoid selecting the '..' directory
  3098.            entry in a normal directory.  */
  3099.         if (rootdir() || this->current_entry != 0)
  3100.         {
  3101.         this->dir_entry[this->current_entry].selected =
  3102.             !this->dir_entry[this->current_entry].selected;
  3103.         this->selected_files +=
  3104.             this->dir_entry[this->current_entry].selected ? 1 : -1;
  3105.         panel_update_entry(this, this->current_entry);
  3106.         }
  3107.  
  3108.         panel_action(this, act_DOWN, link, NULL, repeat_count);
  3109.         break;
  3110.  
  3111.     case act_SELECT_ALL:
  3112.  
  3113.         panel_select_all(this);
  3114.         panel_update_entries(this);
  3115.         done = 1;
  3116.         break;
  3117.  
  3118.     case act_UNSELECT_ALL:
  3119.  
  3120.         panel_unselect_all(this);
  3121.         panel_update_entries(this);
  3122.         done = 1;
  3123.         break;
  3124.  
  3125.     case act_TOGGLE:
  3126.  
  3127.         this->selected_files = 0;
  3128.  
  3129.         for (i = 0; i < this->entries; i++)
  3130.         {
  3131.         if (this->dir_entry[i].type != DIR_ENTRY)
  3132.         {
  3133.             this->dir_entry[i].selected = !this->dir_entry[i].selected;
  3134.             this->selected_files += this->dir_entry[i].selected;
  3135.         }
  3136.         }
  3137.  
  3138.         panel_update_entries(this);
  3139.         done = 1;
  3140.         break;
  3141.  
  3142.     case act_MKDIR:
  3143.  
  3144.         panel_act_MKDIR(this, link);
  3145.         break;
  3146.  
  3147.     case act_MOVE:
  3148.  
  3149.         panel_act_MOVE(this, link);
  3150.         break;
  3151.  
  3152.     case act_UP:
  3153.  
  3154.         need_update_all = need_update = 0;
  3155.  
  3156.         while (repeat_count--)
  3157.         {
  3158.         if (this->current_entry != 0)
  3159.             this->current_entry--;
  3160.         else
  3161.             break;
  3162.  
  3163.         if (this->current_entry + 1 == this->first_on_screen)
  3164.         {
  3165.             this->first_on_screen = max(0, this->first_on_screen -
  3166.                         this->scroll_step);
  3167.             need_update_all = 1;
  3168.         }
  3169.         else
  3170.         {
  3171.             if (!need_update)
  3172.             panel_update_entry(this, this->current_entry + 1);
  3173.  
  3174.             need_update = 1;
  3175.         }
  3176.         }
  3177.  
  3178.         if (need_update_all)
  3179.         panel_update_entries(this);
  3180.         else
  3181.         if (need_update)
  3182.             panel_update_entry(this, this->current_entry);
  3183.         else
  3184.             done = -1;
  3185.         break;
  3186.  
  3187.     case act_DOWN:
  3188.  
  3189.         need_update_all = need_update = 0;
  3190.  
  3191.         while (repeat_count--)
  3192.         {
  3193.         if (this->current_entry < this->entries - 1)
  3194.             this->current_entry++;
  3195.         else
  3196.             break;
  3197.  
  3198.         if (this->current_entry - this->first_on_screen >=
  3199.             this->lines - 2)
  3200.         {
  3201.             this->first_on_screen = min(this->first_on_screen +
  3202.                         this->scroll_step,
  3203.                         this->entries - 1 -
  3204.                         (this->lines - 2) + 1);
  3205.             need_update_all = 1;
  3206.             continue;
  3207.         }
  3208.  
  3209.         if (!need_update)
  3210.             panel_update_entry(this, this->current_entry - 1);
  3211.  
  3212.         need_update = 1;
  3213.         }
  3214.  
  3215.         if (need_update_all)
  3216.         panel_update_entries(this);
  3217.         else
  3218.         if (need_update)
  3219.             panel_update_entry(this, this->current_entry);
  3220.         else
  3221.             done = -1;
  3222.         break;
  3223.  
  3224.     case act_PGUP:
  3225.  
  3226.         if (this->current_entry == 0)
  3227.         {
  3228.         done = -1;
  3229.         break;
  3230.         }
  3231.  
  3232.         old_current_entry = this->current_entry;
  3233.  
  3234.         if (this->current_entry < this->lines - 2)
  3235.         this->current_entry = this->first_on_screen = 0;
  3236.         else
  3237.         {
  3238.         this->current_entry -= this->lines - 2;
  3239.         this->first_on_screen = max(0, this->first_on_screen -
  3240.                            (this->lines - 2));
  3241.         }
  3242.  
  3243.         if (this->entries > this->lines - 2)
  3244.         panel_update_entries(this);
  3245.         else
  3246.         {
  3247.         panel_update_entry(this, old_current_entry);
  3248.         panel_update_entry(this, this->current_entry);
  3249.         }
  3250.  
  3251.         break;
  3252.  
  3253.     case act_PGDOWN:
  3254.  
  3255.         if (this->current_entry == this->entries - 1)
  3256.         {
  3257.         done = -1;
  3258.         break;
  3259.         }
  3260.  
  3261.         old_current_entry = this->current_entry;
  3262.  
  3263.         if (this->entries - 1 - this->first_on_screen < this->lines - 2)
  3264.         this->current_entry = this->entries - 1;
  3265.         else
  3266.         if (this->entries - 1 - this->current_entry < this->lines - 2)
  3267.         {
  3268.             this->current_entry = this->entries - 1;
  3269.             this->first_on_screen = get_fos(this);
  3270.         }
  3271.         else
  3272.         {
  3273.             this->current_entry += this->lines - 2;
  3274.             this->first_on_screen =
  3275.             min(this->first_on_screen + this->lines - 2,
  3276.             (this->entries - 1) - (this->lines - 2) + 1);
  3277.          }
  3278.  
  3279.         if (this->entries > this->lines - 2)
  3280.         panel_update_entries(this);
  3281.         else
  3282.         {
  3283.         panel_update_entry(this, old_current_entry);
  3284.         panel_update_entry(this, this->current_entry);
  3285.         }
  3286.  
  3287.         break;
  3288.  
  3289.     case act_HOME:
  3290.  
  3291.         if (this->current_entry != 0)
  3292.         {
  3293.         this->current_entry = this->first_on_screen = 0;
  3294.         panel_update_entries(this);
  3295.         }
  3296.         break;
  3297.  
  3298.     case act_END:
  3299.  
  3300.         if (this->current_entry != this->entries - 1)
  3301.         {
  3302.         this->current_entry = this->entries - 1;
  3303.         this->first_on_screen = get_fos(this);
  3304.         panel_update_entries(this);
  3305.         }
  3306.         break;
  3307.  
  3308.     case act_CHDIR:
  3309.  
  3310.         panel_act_CHDIR(this, link, (char *)aux_info);
  3311.         break;
  3312.  
  3313.     case act_ENABLE_NEXT_MODE:
  3314.  
  3315.         this->display_mode = (this->display_mode + 1) % FILE_DISPLAY_MODES;
  3316.         goto all_display_modes;
  3317.  
  3318.     case act_ENABLE_OWNER_GROUP:
  3319.     case act_ENABLE_DATE_TIME:
  3320.     case act_ENABLE_SIZE:
  3321.     case act_ENABLE_MODE:
  3322.     case act_ENABLE_FULL_NAME:
  3323.     case act_ENABLE_ALL:
  3324.  
  3325.         this->display_mode = action - act_ENABLE_OWNER_GROUP;
  3326.  
  3327.       all_display_modes:
  3328.  
  3329.         /* Avoid displaying the ENABLE_ALL mode when not the number of
  3330.            columns is not big enough (we are in two panel mode).  */
  3331.         if (this->columns < 80 && this->display_mode == ENABLE_ALL)
  3332.         this->display_mode = ENABLE_OWNER_GROUP;
  3333.  
  3334.         panel_update_entries(this);
  3335.         break;
  3336.  
  3337.     case act_SORT_NEXT_METHOD:
  3338.  
  3339.         this->sort_method = (this->sort_method + 1) % FILE_SORT_METHODS;
  3340.         goto all_sort_methodes;
  3341.  
  3342.     case act_SORT_BY_NAME:
  3343.     case act_SORT_BY_EXTENSION:
  3344.     case act_SORT_BY_SIZE:
  3345.     case act_SORT_BY_DATE:
  3346.     case act_SORT_BY_MODE:
  3347.     case act_SORT_BY_OWNER_ID:
  3348.     case act_SORT_BY_GROUP_ID:
  3349.     case act_SORT_BY_OWNER_NAME:
  3350.     case act_SORT_BY_GROUP_NAME:
  3351.  
  3352.         this->sort_method = action - act_SORT_BY_NAME;
  3353.  
  3354.       all_sort_methodes:
  3355.  
  3356.         CurrentSortMethod = this->sort_method;
  3357.  
  3358.         /* Check if this is the root directory and sort without the
  3359.            ".." entry if it is.  */
  3360.         if (this->path[1] == 0)
  3361.         qsort(this->dir_entry, this->entries,
  3362.               sizeof(dir_entry_t), qcompare);
  3363.         else
  3364.         qsort(this->dir_entry + 1, this->entries - 1,
  3365.               sizeof(dir_entry_t), qcompare);
  3366.  
  3367.         panel_update_entries(this);
  3368.         break;
  3369.  
  3370.     case act_SWITCH:
  3371.  
  3372.         xchg(&this->lines,   &link->lines);
  3373.         xchg(&this->columns, &link->columns);
  3374.         xchg(&this->x,       &link->x);
  3375.         xchg(&this->y,       &link->y);
  3376.  
  3377.         window_end(this->window);
  3378.         this->window = window_init(this->x, this->y,
  3379.                        this->lines, this->columns);
  3380.         window_end(link->window);
  3381.         link->window = window_init(link->x, link->y,
  3382.                        link->lines, link->columns);
  3383.  
  3384.         break;
  3385.  
  3386.     case act_PATTERN_SELECT:
  3387.     case act_PATTERN_UNSELECT:
  3388.  
  3389.         for (i = 0; i < this->entries; i++)
  3390.         if (this->dir_entry[i].type != DIR_ENTRY)
  3391.         {
  3392.             int fnm_flags = FNM_PATHNAME;
  3393.  
  3394.             if (LeadingDotMatch == OFF)
  3395.             fnm_flags |= FNM_PERIOD;
  3396.  
  3397.             if (fnmatch(aux_info,this->dir_entry[i].name,fnm_flags)==0)
  3398.             {
  3399.             switch (action)
  3400.             {
  3401.                 case act_PATTERN_SELECT:
  3402.  
  3403.                 if (!this->dir_entry[i].selected)
  3404.                 {
  3405.                     this->dir_entry[i].selected = 1;
  3406.                     this->selected_files++;
  3407.                 }
  3408.                 break;
  3409.  
  3410.                 case act_PATTERN_UNSELECT:
  3411.  
  3412.                 if (this->dir_entry[i].selected)
  3413.                 {
  3414.                     this->dir_entry[i].selected = 0;
  3415.                     this->selected_files--;
  3416.                 }
  3417.                 break;
  3418.             }
  3419.             }
  3420.         }
  3421.  
  3422.         panel_update_entries(this);
  3423.         done = 1;
  3424.         break;
  3425.  
  3426.     case act_REFRESH:
  3427.  
  3428.         panel_act_REFRESH(this, aux_info);
  3429.         done = -1;
  3430.         break;
  3431.  
  3432.     case act_SET_SCROLL_STEP:
  3433.  
  3434.         {
  3435.         int scroll_step = atoi((char *)aux_info);
  3436.  
  3437.         if (scroll_step > 0 && scroll_step < this->lines - 1)
  3438.             this->scroll_step = link->scroll_step = scroll_step;
  3439.         }
  3440.         break;
  3441.  
  3442.     case act_ISEARCH_BEGIN:
  3443.  
  3444.         this->isearch_stack = xstack_init(sizeof(isearch_t));
  3445.  
  3446.         STACK_PUSH(this->current_entry, 0);
  3447.  
  3448.         this->isearch_length  = 0;
  3449.         this->wrapped_isearch = 0;
  3450.  
  3451.         break;
  3452.  
  3453.     case act_ISEARCH_BACKWARD:
  3454.  
  3455.         iai = (isearch_aux_t *)aux_info;
  3456.         len = strlen(iai->string);
  3457.  
  3458.         switch (iai->action)
  3459.         {
  3460.         case IL_ISEARCH_ACTION_NONE:
  3461.             break;
  3462.  
  3463.         case IL_ISEARCH_ACTION_DECREASE:
  3464.  
  3465.             goto isearch_action_decrease;
  3466.  
  3467.         case IL_ISEARCH_ACTION_RETRY:
  3468.  
  3469.             if (!this->wrapped_isearch)
  3470.             STACK_PUSH(this->current_entry, len);
  3471.  
  3472.             /* Search backward.  */
  3473.             result = panel_isearch_backward(this, iai->string, len,
  3474.                             this->wrapped_isearch ?
  3475.                             this->entries - 1 :
  3476.                             this->current_entry - 1);
  3477.  
  3478.             goto isearch_backward_action_increase;
  3479.  
  3480.         case IL_ISEARCH_ACTION_INCREASE:
  3481.  
  3482.             STACK_PUSH(this->current_entry, len);
  3483.  
  3484.             /* Search backward.  */
  3485.             result = panel_isearch_backward(this, iai->string, len,
  3486.                             this->current_entry);
  3487.  
  3488.           isearch_backward_action_increase:
  3489.  
  3490.             if (result == -1)
  3491.             {
  3492.             iai->action = IL_ISEARCH_ACTION_FAILED;
  3493.             break;
  3494.             }
  3495.             else
  3496.             this->isearch_length = len;
  3497.  
  3498.             if (this->wrapped_isearch)
  3499.             {
  3500.             panel_set_position(this, result);
  3501.             panel_update_entries(this);
  3502.             this->wrapped_isearch = 0;
  3503.             }
  3504.             else
  3505.             panel_action(this, act_UP, link, NULL,
  3506.                      this->current_entry - result);
  3507.  
  3508.             break;
  3509.  
  3510.         default:
  3511.  
  3512.             break;
  3513.         }
  3514.  
  3515.         iai->length = this->isearch_length;
  3516.         break;
  3517.  
  3518.     case act_ISEARCH_FORWARD:
  3519.  
  3520.         iai = (isearch_aux_t *)aux_info;
  3521.         len = strlen(iai->string);
  3522.  
  3523.         switch (iai->action)
  3524.         {
  3525.         case IL_ISEARCH_ACTION_NONE:
  3526.             break;
  3527.  
  3528.         case IL_ISEARCH_ACTION_DECREASE:
  3529.  
  3530.           isearch_action_decrease:
  3531.             {
  3532.             int    prev_entry;
  3533.             size_t prev_length;
  3534.  
  3535.             /* Undo the last step of isearch-forward.  */
  3536.             STACK_POP(prev_entry, prev_length);
  3537.  
  3538.             if (this->isearch_length != len)
  3539.                 break;
  3540.  
  3541.             if (this->current_entry < prev_entry)
  3542.                 panel_action(this, act_DOWN, link, NULL,
  3543.                          prev_entry - this->current_entry);
  3544.             else
  3545.                 if (this->current_entry > prev_entry)
  3546.                 panel_action(this, act_UP, link, NULL,
  3547.                          this->current_entry - prev_entry);
  3548.  
  3549.             STACK_PREVIEW(prev_entry, prev_length);
  3550.  
  3551.             this->isearch_length = prev_length;
  3552.             }
  3553.  
  3554.             break;
  3555.  
  3556.         case IL_ISEARCH_ACTION_RETRY:
  3557.  
  3558.             if (!this->wrapped_isearch)
  3559.             STACK_PUSH(this->current_entry, len);
  3560.  
  3561.             /* Search forward.  */
  3562.             result = panel_isearch_forward(this, iai->string, len,
  3563.                            this->wrapped_isearch ?
  3564.                            0 :
  3565.                            this->current_entry + 1);
  3566.  
  3567.             goto isearch_forward_action_increase;
  3568.  
  3569.         case IL_ISEARCH_ACTION_INCREASE:
  3570.  
  3571.             STACK_PUSH(this->current_entry, len);
  3572.  
  3573.             /* Search forward.  */
  3574.             result = panel_isearch_forward(this, iai->string, len,
  3575.                            this->current_entry);
  3576.  
  3577.           isearch_forward_action_increase:
  3578.  
  3579.             if (result == -1)
  3580.             {
  3581.             iai->action = IL_ISEARCH_ACTION_FAILED;
  3582.             break;
  3583.             }
  3584.             else
  3585.             this->isearch_length = len;
  3586.  
  3587.             if (this->wrapped_isearch)
  3588.             {
  3589.             panel_set_position(this, result);
  3590.             panel_update_entries(this);
  3591.             this->wrapped_isearch = 0;
  3592.             }
  3593.             else
  3594.             panel_action(this, act_DOWN, link, NULL,
  3595.                      result - this->current_entry);
  3596.  
  3597.             break;
  3598.  
  3599.         default:
  3600.  
  3601.             break;
  3602.         }
  3603.  
  3604.         iai->length = this->isearch_length;
  3605.         break;
  3606.  
  3607.     case act_ISEARCH_END:
  3608.  
  3609.         xstack_end(this->isearch_stack);
  3610.         this->isearch_stack = NULL;
  3611.         break;
  3612.  
  3613.     default:
  3614.  
  3615.         fatal("no action");
  3616.         break;
  3617.     }
  3618.  
  3619.     if (done != -1)
  3620.     panel_update_info(this);
  3621.  
  3622.     return done;
  3623. }
  3624.