home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / file / managers / git-4.3 / git-4 / git-4.3.7 / src / panel.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-09  |  86.6 KB  |  3,470 lines

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