home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume26 / maint / part07 / main.c
Encoding:
C/C++ Source or Header  |  1992-05-13  |  76.4 KB  |  2,588 lines

  1. /******************************************************************************
  2. *******************************************************************************
  3.  
  4.    Site:    Western Michigan University Academic Computer Center
  5.  
  6.    System:    Directory/File System Maintenance
  7.   
  8.    Program:    maint
  9.  
  10.    Version=01    Level=00    01/24/92    Leonard J. Peirce
  11.  
  12.    Purpose:    This program provides the user with a full-screen method for
  13.         maintaining directories and file systems.  The directory
  14.         will be presented to the user in a series of screens, each
  15.         of which will contain information about individual files.
  16.         The user may move through a directory structure, marking files
  17.         for deletion, changing protection modes, renaming files,
  18.         creating text descriptors, copying files, editing files, and
  19.         viewing files.  Other options are available to allow the user
  20.         to fork a subprocess to return to shell and search for a
  21.         file.
  22.  
  23.         See the manual page maint.1 for more details.
  24.  
  25.    Arguments:
  26.  
  27.         -a            auto-advance the cursor on
  28.                     Delete or Unmark
  29.         -c            ask for confirmation before executing
  30.         -d            include date field for files
  31.         -f            full info; implies -ogds
  32.         -g            include group name for files
  33.         -o            include owner field for files
  34.         -p            include protection field for files
  35.         -s            include size (in bytes) for files
  36.         -t            start up in text mode
  37.         -S <sort-opt>        sort files by <sort-opt>; legal
  38.                     values are:  size, date, protection,
  39.                     filename; default is filename
  40.  
  41.    External variables:    See individual routine(s)
  42.  
  43.    Maint external functions:
  44.  
  45.     Defined:    None
  46.  
  47.     Called:        cat, check_marks, clean_up, clear_mess, config_options,
  48.             create_text, edit, erase_tag, file_locate,
  49.             file_ptr_locate, file_select, follow_link, free_comm,
  50.             free_pool, get_args, get_bnum, get_dir, get_dir_mem,
  51.             get_filemarks, get_num_file, get_scr_num, give_help,
  52.             highlite, info, init_pool, initialize, make_screen,
  53.             make_slot, mark_cancel, mark_copy_ren, mark_delete,
  54.             mark_group, mark_owner, mark_protect, mark_rename,
  55.             mark_repeat, mark_text, prompt_getstr, put_pagnum,
  56.             put_slot, put_spec, put_stat, put_text, read_text_des,
  57.             screen_reset, set_args, set_nodes, set_screen,
  58.             sort_files, strindex, strmcpy, strdel, tag_file, xecute
  59.  
  60.    Files accessed:    only those in the directory and possibly .maint.tdf
  61.             in other directories
  62.  
  63.    Return codes:    0    Successful completion
  64.  
  65.    Compiling instructions:    See Makefile.
  66.  
  67.    Linking instructions:    See Makefile.
  68.  
  69.    Other information:    (C) Copyright 1992, Leonard J. Peirce
  70.  
  71.    Data structures:
  72.  
  73.    Several different data structures are used internally to Maint to store
  74.    all kinds of information, ranging from the filenames and related information
  75.    to text descriptors and new filenames.
  76.  
  77.    Directory information for the files in a directory is allocated dynamically
  78.    upon entry to the proc_dir() function.  An automatic pointer, dirptr, is
  79.    used to point to the memory that holds the information about the files.
  80.    This pointer MUST remain automatic because each directory needs it own
  81.    copy.  After the number of files in the directory is determined, the
  82.    amount of memory needed to hold the information about each file in the
  83.    directory can be calculated.  It is calculated by taking the number of
  84.    files in the directory, adding one, and multiplying it by the length of
  85.    the structure definition ENT_DEF.  This is the number of bytes needed.
  86.    The memory is allocated by calling malloc() and the pointer to the
  87.    memory is stored in dirptr.
  88.  
  89.    When a Finish command is issued or the commands in a directory are executed,
  90.    the memory is deallocated, the number of files is computed, the size of
  91.    the memory necessary is calculated, and the memory is allocated once again.
  92.  
  93.    A) Memory Pools
  94.  
  95.    MAINT uses its own memory pool allocation scheme.  These memory pools are
  96.    used to store only CHARACTER data and thus there is no need to worry about
  97.    byte alignment for integer values and such.  Items that are stored in the
  98.    memory pools include:  1) the full filename of a file, and 2) the text
  99.    descriptor of the file in case the user selects Text mode.
  100.  
  101.    The pools are allocated on a demand basis and are linked together into a
  102.    linked list.  The size of each memory pool in the list can vary, depending
  103.    on the state of the information that will be included for each directory.
  104.    The global variable ent_factor is initially set to FUDGE_FACTOR, which is
  105.    a guess at the number of bytes that each file entry is going to need to
  106.    store its information.  Then the run-time arguments are taken into account,
  107.    with ent_factor being updated for each that argument that will require
  108.    something stored in the memory pools.  See the routine set_factors() for
  109.    the values that ent_factor is increased by.
  110.  
  111.    When it is time to redo the information for a directory, the memory in the
  112.    memory pools is reclaimed rather than released and reallocated.  The pointer 
  113.    to the next available byte in the structure is set to point to the beginning
  114.    of the memory pool and the remaining length is reset to the original length
  115.    of the pool.  This may waste a little memory but it saves a few calls that
  116.    would be needed to free and reallocate the memory.
  117.  
  118.    B) Command structures
  119.  
  120.    Command structures are used to hold the information necessary to carry out
  121.    commands for a specific file.  When the first command for a file is speci-
  122.    fied by the user, a new command structure is allocated and linked to the
  123.    file entry for the file, the pointer to the command structure being stored
  124.    in the command field of the ENT_DEF structure.
  125.  
  126.    All of the information in the command structure that requires string values
  127.    (such as ren_name, copy_name, and text) are stored in memory allocated by
  128.    malloc() and NOT in a memory pool.  This is to cut down on wasting of
  129.    memory for file commands that are cancelled.
  130.  
  131.    When commands for a file are cancelled, all the memory for any of the char-
  132.    acter strings are freed (via free()) and the command structure itself
  133.    is freed.
  134.  
  135. ********************************************************************************
  136. *******************************************************************************/
  137.  
  138. /******************************************************************************/
  139. /*                                                                            */
  140. /*                        # I N C L U D E   F I L E S                         */
  141. /*                                                                            */
  142. /******************************************************************************/
  143.  
  144. #ifdef ultrix
  145. #include <cursesX.h>
  146. #include <unistd.h>
  147. #else
  148. #include <curses.h>
  149. #include <sys/file.h>
  150. #endif
  151. #include <sys/param.h>
  152. #include <sys/stat.h>
  153. #include <stdio.h>            /* who knows?    (NULL maybe?)          */
  154. #include <time.h>            /* for getting current year value     */
  155. #include <string.h>            /* string functions              */
  156. #include <malloc.h>            /* so free is declared right          */
  157. #include "maint.h"            /* our very own header file          */
  158.  
  159. /******************************************************************************/
  160. /*                                                                            */
  161. /*                             # D E F I N E S                                */
  162. /*                                                                            */
  163. /******************************************************************************/
  164.  
  165. #define MAX_DIR 15            /* max. # of subdirectories to save   */
  166.  
  167. /******************************************************************************/
  168. /*                                                                            */
  169. /*          S T R U C T U R E S ,   U N I O N S ,   T Y P E D E F S           */
  170. /*                                                                            */
  171. /******************************************************************************/
  172.  
  173. /******************************************************************************/
  174. /*                                                                            */
  175. /*   E X T E R N A L   D E F I N I T I O N S   &   D E C L A R A T I O N S    */
  176. /*                                                                            */
  177. /******************************************************************************/
  178.  
  179. extern     char      *getenv();
  180.  
  181. extern     u_int      sleep();
  182.  
  183. extern     int      strindex(),
  184.           access();
  185.  
  186. extern     long      get_bnum();
  187.  
  188. extern     int      mark_text(),
  189.           mark_delete(),
  190.           mark_copy_ren(),
  191.           mark_protect(),
  192.           mark_rename(),
  193.           mark_repeat(),
  194.           mark_owner(),
  195.           mark_group(),
  196.           check_marks(),
  197.           put_slot(),
  198.           file_select(),
  199.           edit(),
  200.           make_slot(),
  201.           read_text_des(),
  202.           file_locate(),
  203.           file_ptr_locate(),
  204.           follow_link(),
  205.           config_options(),
  206.           create_text();
  207.  
  208. extern     short      info(),
  209.           get_num_file(),
  210.           get_scr_num();
  211.  
  212. extern     void      mark_cancel(),
  213.           free_comm(),
  214.           set_screen(),
  215.           set_args(),
  216.           set_nodes(),
  217.           get_args(),
  218.           get_dir_mem(),
  219.           get_dir(),
  220.           make_screen(),
  221.           init_pool(),
  222.           free_pool(),
  223.           initialize(),
  224.           put_stat(),
  225.           put_spec(),
  226.           put_pagnum(),
  227.           prompt_getstr(),
  228.           screen_reset(),
  229.           clear_mess(),
  230.           highlite(),
  231.           tag_file(),
  232.           erase_tag(),
  233.           clean_up(),
  234.           put_text(),
  235.           xecute(),
  236.           get_text(),
  237.           get_filemarks(),
  238.           give_help();
  239.  
  240. #if defined(SYSV) && !defined(sun)
  241.  
  242. extern     char      *getcwd();
  243. extern     u_short  geteuid();
  244.  
  245. #else
  246.  
  247. extern     char      *getwd();
  248.  
  249. #endif
  250.  
  251.      ENT_DEF  *baseptr;        /* pointer to directory entries          */
  252.  
  253.      WINDOW      *spec_win,        /* directory spec/page # window          */
  254.           *main_win,        /* main display window              */
  255.           *stat_win;        /* stats/options window              */
  256.  
  257.      int      *global_row,        /* so others functions can use it     */
  258.           *global_col,        /* ditto....                  */
  259.            curr_year,        /* current year value              */
  260.           main_rows;        /* rows in main window              */
  261.  
  262.      u_short  node_row_max,        /* max. # of rows in nodes array      */
  263.           node_col_max,        /* max. # of columns in nodes array   */
  264.           cont_flag;        /* set if continuing after suspend    */
  265.  
  266.      ARG_DEF  args;            /* run-time argument flags          */
  267.  
  268.      NODE_DEF nodes[MAX_NODE_ROW+1][MAX_NODE_COL+1];
  269.  
  270.      ARG_STR  arg_strings[] = {
  271. {"Include file size? (-s)                 ",&args.size},
  272. {"Include file protection? (-p)           ",&args.prot},
  273. {"Include date? (-d)                      ",&args.date},
  274. {"Include group? (-g)                     ",&args.group},
  275. {"Include owner? (-o)                     ",&args.owner},
  276. {"Confirm before Xecuting marks? (-c)     ",&args.confirm},
  277. {"Auto-feed on Delete/Unmark? (-A)        ",&args.auto_feed},
  278. {"Include text descriptors? (-t)          ",&args.text},
  279. {"Include dot-files? (-a)                 ",&args.dot_files},
  280. {"Include file marks? (-F)                ",&args.filemarks},
  281. {"Sort field? (-S)                        ",&args.sort}
  282. };
  283.  
  284. /******************************************************************************/
  285. /*                                                                            */
  286. /*     S T A T I C   D E F I N I T I O N S   &   D E C L A R A T I O N S      */
  287. /*                                                                            */
  288. /******************************************************************************/
  289.  
  290. static     DIR_PTR  dir_ent_head,        /* head of directory node info list   */
  291.           dir_ent_tail,        /* tail.......                  */
  292.           *link_dir_ent(),    /* link active directory node          */
  293.           *find_dir();        /* for searching the list          */
  294.  
  295. static     size_t      ent_factor;        /* ent. size memory allocation factor */
  296.  
  297. static     int      home_len,        /* length of home_dir              */
  298.           get_year(),        /* get current year value          */
  299.           proc_dir();        /* process current directory          */
  300.  
  301. static     short      def_slot_wid;        /* default slot width              */
  302.  
  303. static     char      home_dir[MAXPATHLEN+3];
  304.  
  305. static     void      unlink_dir_ent();    /* unlink active directory node          */
  306.          
  307. /******************************************************************************/
  308. /*                                                                            */
  309. /*                        M A I N   P R O C E S S I N G                       */
  310. /*                                                                            */
  311. /******************************************************************************/
  312.  
  313. main(argc,argv)
  314.                     /*******   FORMAL  PARAMETERS   *******/
  315.      int      argc;            /* number of run-time arguments          */
  316.      char      **argv;        /* run-time argument strings          */
  317.  
  318. {    /*** main ***/
  319.                     /********   LOCAL  VARIABLES   ********/
  320.      short      retval;        /* return value from proc_dir()          */
  321.  
  322.  
  323.    /* check the command-line arguments and set up 1) the screen width and 2)
  324.     * the slot width
  325.     */
  326.  
  327.    get_args(argc,argv,&args);
  328.  
  329.    curr_year = get_year();        /* get the current year              */
  330.  
  331.    strcpy(home_dir,getenv("HOME"));
  332.    home_len = strlen(home_dir);
  333.  
  334.    /* initialize/allocate all of the windows that will be needed and get
  335.     * the necessary control strings from the termcap database
  336.     */
  337.  
  338.    initialize(&spec_win,&main_win,&stat_win,&node_row_max,&node_col_max);
  339.  
  340.    /* initialize some global factors, such as the ent_factor and default
  341.     * slot width
  342.     */
  343.  
  344.    set_args(&args,&def_slot_wid,&ent_factor);
  345.  
  346.    /* set up the active directory linked list */
  347.  
  348.    dir_ent_head.next = &dir_ent_tail;
  349.    dir_ent_head.prev = &dir_ent_tail;
  350.    dir_ent_tail.next = &dir_ent_head;
  351.    dir_ent_tail.prev = &dir_ent_head;
  352.  
  353.    /* go for it -- process the directories and any thing else that the
  354.     * user might desire
  355.     */
  356.  
  357.    do
  358.    {
  359.       retval = proc_dir();
  360.    }
  361.    while(retval == RECALL_PROC);
  362.  
  363.    clean_up();                /* reset things                  */
  364.  
  365. }    /*** main ***/
  366.  
  367. /*******************************************************************************
  368. ********************************************************************************
  369.  
  370.   Function:    proc_dir
  371.  
  372.   Purpose:    Process the current directory.  This includes allocating memory
  373.         for storing the information about the files and handling all
  374.         of the user responses.  This routine is recursive and is called
  375.         each time the user "selects" a new directory; the current dir-
  376.         ectory is switched and this routine is called again.  When the
  377.         user exits a directory, this routine frees up the memory that
  378.         was allocated for the directory and returns.  The cursor is
  379.         left pointing to the directory that was just exited.
  380.  
  381.         This is a *RECURSIVE* function.
  382.  
  383.   Global variables:
  384.  
  385.     Name            Examine/Modify/Use/Read/Write
  386.     ----            -----------------------------
  387.     args               X        X
  388.     user_dir                X
  389.     ent_factor                X
  390.     def_slot_wid              X    X
  391.     main_rows                X
  392.     main_win                X
  393.     home_dir                X
  394.     home_len                X
  395.     node_row_max                X
  396.     dir_ent_head                X
  397.     dir_ent_tail                X
  398.  
  399.   Return Codes:
  400.  
  401.     Code            Reason
  402.     ----            ------
  403.          0            return normally
  404.     RECALL_PROC        return and force this procedure to be recalled
  405.                 immediately
  406.     EMPTY_DIR        the directory entry selected to get us here is
  407.                 empty
  408.  
  409. ********************************************************************************
  410. *******************************************************************************/
  411.  
  412. static int proc_dir()
  413.  
  414. {      /*** proc_dir ***/
  415.                     /*********   LOCAL VARIABLES   ********/
  416. register int      term_code;        /* code for keystroke              */
  417.      DIR_PTR  *dir_ent_ptr;        /* pointer to directory info node     */
  418.      ENT_DEF  *dirptr,        /* pointer to directory information   */
  419.           *entptr,        /* pointer to directory entry          */
  420.           *prev_ptr;        /* ptr to file from previous command  */
  421.      POOL_DEF *first_pool,        /* pointer to first memory pool          */
  422.           *curr_pool;        /* pointer to current memory pool     */
  423.      char      *dir_text= NULL,     /* pointer to directory text descrip  */
  424.           *dir_name;        /* current directory name          */
  425.      long      num_block = 0L;    /* number of blocks in directory      */
  426.      u_int      dirsize;        /* size of directory info memory      */
  427.      size_t      pool_length;        /* length for allocating new pools    */
  428.      time_t      stat_time;        /* last mod time for directory          */
  429.      int      scr_col,        /* current screen column          */
  430.                scr_row;        /* current screen row              */
  431.      u_short  prev_command;        /* last command -- used for repeating */
  432.      short      num_col,        /* number of columns for full screen  */
  433.           node_col,        /* current node array column          */
  434.            node_row,        /* current node array row          */
  435.           slot_width,        /* slot width for current directory   */
  436.           num_row,        /* number of rows for full screen     */
  437.                num_file = 0,        /* number of files in directory          */
  438.                curr_screen = 1,    /* current screen of directory          */
  439.                num_screen = 0,    /* number of screens in directory     */
  440.                  scr_file,        /* number of files for current screen */
  441.                num_slot,        /* number of slots per screen          */
  442.           branched = FALSE,    /* TRUE if we Branched to a directory */
  443.           filemarks_flag = 0,    /* TRUE if marks processed in dir     */
  444.           sort_flag = 0,    /* TRUE if entries have been sorted   */
  445.            text_flag = 0;    /* text descrip file processed flag   */
  446.                     /** above vars MUST remain automatic **/
  447. static     COM_DEF  *comm_ptr;        /* for saving pointer to commands     */
  448. static     char      *tptr;        /* temporary character pointer          */
  449. static     int      curr_type,        /* type of current file              */
  450.            status;        /* return code status              */
  451. static     short      temp,            /* temporary value              */
  452.           temp2,        /* temporary value #2              */
  453.           save_width,        /* temporary slot width holder          */
  454.           change_dir,        /* set if we're changing directories  */
  455.           new_screen,        /* new screen when locating files     */
  456.           level;        /* what recursion level we are at     */
  457. static     DIR_SUMM dirmem[MAX_DIR];    /* summary info for all directories   */
  458. static     struct      stat   statbuf;    /* for getting access time for dir    */
  459. static     u_char      mess_flag,        /* set if information mess on screen  */
  460.                cursor_flag,        /* set if cursor needs to be set      */
  461.           exec_flag,        /* set if we just executed commands   */
  462.           pass;            /* for one-time execution of stuff    */
  463. static     char      slot_buf[BUFSIZ];    /* for formatting screen slots          */
  464.  
  465.  
  466.    cursor_flag = 0;
  467.    prev_ptr = NULL;            /* no previous command yet          */
  468.    slot_width = def_slot_wid;        /* initialize the slot width          */
  469.    num_row = node_row_max;        /* set number of rows for now          */
  470.  
  471.    /* this is kind of weird; we need to get the directory name soon after
  472.     * entering proc_dir() and BEFORE calling get_num_file() in case we hit
  473.     * hit an empty directory; the reason is that we might be coming backing
  474.     * in to a directory that was previously non-empty and thus would need to
  475.     * find the node in the active directory list for this directory and delete
  476.     * it; and since we search the list by directory, we need the directory
  477.     * name; grrr......
  478.     */
  479.  
  480.    dir_name = (char *) malloc(MAXPATHLEN+3);
  481.  
  482. #if defined(SYSV) && !defined(sun)
  483.    getcwd(dir_name,MAXPATHLEN+2);
  484. #else
  485.    getwd(dir_name);
  486. #endif
  487.  
  488.    /* find the active node for this directory, if it exists */
  489.  
  490.    dir_ent_ptr = find_dir(&dir_ent_head,&dir_ent_tail,dir_name);
  491.  
  492.    num_file = get_num_file(&args);    /* get number of files in directory   */
  493.  
  494.    if(num_file == 0 || stat(".",&statbuf) != 0)
  495.    {
  496.       /* have we been here before?  if so, we need to free up the memory
  497.        * pools
  498.        */
  499.  
  500.       if(dir_ent_ptr != NULL)
  501.       {
  502.      free_pool(dir_ent_ptr->first_pool);
  503.      dir_ent_ptr->num_file = 0;    /* gone but not forgotten          */
  504.      unlink_dir_ent(dir_ent_ptr);
  505.       }
  506.  
  507.       return(EMPTY_DIR);
  508.    }
  509.  
  510.    stat_time = statbuf.st_ctime;    /* save time directory last changed   */
  511.    sort_flag = args.sort;
  512.  
  513.    if(dir_ent_ptr != NULL)
  514.    {
  515.       /* this directory is currently active */
  516.  
  517.       free(dir_name);            /* don't need this anymore          */
  518.       dirptr = dir_ent_ptr->dirptr;
  519.       dir_name = dir_ent_ptr->dir_name;
  520.       first_pool = dir_ent_ptr->first_pool;
  521.       curr_pool = dir_ent_ptr->curr_pool;
  522.       pool_length = dir_ent_ptr->pool_length;
  523.       num_file = dir_ent_ptr->num_file;
  524.       dir_ent_ptr->count += 1;        /* update the active count          */
  525.    }
  526.    else
  527.    {
  528.       /* this directory currently has no activity; this *doesn't* mean that
  529.        * it hasn't seen before; it just means that the user hasn't visited
  530.        * it, Branched out, and returned to it
  531.        */
  532.  
  533.       /* get some memory to hold info about individual files */
  534.  
  535.       get_dir_mem(&dirptr,&dirsize,num_file);
  536.  
  537.       /* initialize the memory pools for holding the full filenames of the
  538.        * files in the directory
  539.        */
  540.  
  541.       pool_length = (num_file + 1) * ent_factor;
  542.       init_pool(&first_pool,pool_length);
  543.       curr_pool = first_pool;        /* set current pool pointer          */
  544.                                          
  545.       /* get all of the information about the files in the directory */
  546.              
  547.       get_dir(dirptr,&args,&num_block,&curr_pool,&num_file,pool_length);
  548.  
  549.       /* were there any files to get?  there might not be, depending on the
  550.        * volatility of the directory and file protections
  551.        */
  552.  
  553.       if(!num_file)
  554.       {
  555.      free_pool(first_pool);        /* free up the memory pools first     */
  556.  
  557.      /* now free up the memory for the directory information */
  558.  
  559.      free((char *) dirptr);
  560.      return(EMPTY_DIR);
  561.       }
  562.  
  563.       get_text(&args,dirptr,&curr_pool,&dir_text,&mess_flag,&slot_width,
  564.            &text_flag,pool_length,num_file);
  565.  
  566.       /* make a directory node entry and add it to the list */
  567.  
  568.       dir_ent_ptr = link_dir_ent(&dir_ent_head,&dir_ent_tail,dirptr,dir_name,
  569.                  first_pool,curr_pool,pool_length,stat_time,
  570.                  num_file);
  571.    }
  572.  
  573.    global_row = &scr_row;        /* mostly for handling signals          */
  574.    global_col = &scr_col;
  575.  
  576.    dirmem[level].ptr = dirptr;        /* save a pointer to this memory      */
  577.    dirmem[level].num_file = num_file;
  578.  
  579.    /* count this recursion level; when it equals one, it means that we are in
  580.     * the top-level directory for this session
  581.     */
  582.  
  583.    level++;
  584.  
  585.    /* all of the information about the files in the directory has been
  586.     * obtained; now initialize the screen parameters, including the screen
  587.     * nodes and the number of screens for the directory
  588.     */
  589.  
  590.    set_screen(&num_screen,&scr_file,num_file,slot_width,node_row_max,&num_col);
  591.  
  592.    num_slot = scr_file;            /* save number of slots per screen    */
  593.  
  594.    /* initialize the screen slot node pointers for the screen */
  595.  
  596.    set_nodes(nodes,node_row_max,&scr_file,num_screen,curr_screen,num_file,
  597.          slot_width,num_col);
  598.  
  599.    /* write the directory statistics line at the bottom of the display
  600.     * and the directory spec/page number line at the top of the display
  601.     */
  602.  
  603.    put_spec(spec_win,dir_name,home_dir,home_len,num_screen);
  604.    put_stat(stat_win,num_block,num_file);
  605.  
  606.    if(args.text)            /* write text descrip for directory?  */
  607.       put_text(spec_win,dir_text);
  608.  
  609.    /* clear the main virtual display; we do this every time we enter a
  610.     * directory to make things simple; some directories might have text
  611.     * descriptors and some might not; this way, we make sure that we have
  612.     * a clean display to work with, no matter what
  613.     */
  614.  
  615.    werase(main_win);
  616.  
  617.    /* put the information about the files for the first screen on the screen */
  618.              
  619.    make_screen(main_win,nodes,dirptr,&args,curr_screen,node_row_max,
  620.            node_col_max,scr_file,num_slot,slot_width,text_flag);
  621.  
  622.    /* position the cursor to the first file in the directory */
  623.  
  624.    node_row = 0;            /* start in top left of nodes array   */
  625.    node_col = 0;
  626.    scr_row = (int) nodes[0][0].row;    /* start in top left of screen, too   */
  627.    scr_col = (int) nodes[0][0].column;
  628.  
  629.    /* write the options line only once */
  630.  
  631.    if(!pass)
  632.    {
  633.       put_options(stat_win);
  634.       pass++;
  635.    }
  636.  
  637.    /* set the cursor position to the first file */
  638.  
  639.    wmove(main_win,scr_row,scr_col);
  640.    wrefresh(main_win);
  641.  
  642.    /* this is the main processing loop for the whole program; each keystroke
  643.     * is read and appropriate action is taken for each; this is an infinite
  644.     * loop and the only way out is to break out from the middle and return
  645.     * to a previous invocation of this function
  646.     */
  647.  
  648.    for(;;)                   /* loop forever.........          */
  649.    {
  650.       /* get a keystroke from the keyboard (store in term_code) */
  651.  
  652.       term_code = wgetch(main_win);
  653.  
  654.       if(mess_flag)            /* was a message on the screen?          */
  655.       {
  656.      clear_mess(main_win);        /* clear the message              */
  657.      mess_flag = 0;            /* clear the message flag          */
  658.  
  659.      /* reset the cursor to where it should be */
  660.  
  661.      wmove(main_win,scr_row,scr_col);
  662.       }
  663.  
  664.       /* now look at the key that was pressed and take appropriate action */
  665.  
  666.       switch(term_code)
  667.       {
  668.      case(CONTROL_I):        /**************************************/
  669.      case(' '):            /*             right-arrow          */
  670.      case('>'):            /**************************************/
  671.      case('L'):
  672.      case('l'):
  673.      case(KEY_RIGHT):
  674.  
  675.         /* update the pointer to the screen and then update the position
  676.          * of the physical cursor; we must use temp to get the up_row
  677.          * position and then assign it to node_row because we can't have
  678.          * node_row changing before we use it to get node_col
  679.          */
  680.  
  681.         temp = (short) nodes[node_row][node_col].right_row;
  682.         node_col = (short) nodes[node_row][node_col].right_col;
  683.         node_row = temp;
  684.         scr_row = (int) nodes[node_row][node_col].row;
  685.         scr_col = (int) nodes[node_row][node_col].column;
  686.  
  687.         cursor_flag++;        /* reset the cursor down below          */
  688.         break;
  689.  
  690.      case(BACKSPACE):        /**************************************/
  691.      case('<'):            /*             left-arrow          */
  692.      case('H'):            /**************************************/
  693.      case(KEY_BACKSPACE):
  694.      case('h'):
  695.      case(KEY_LEFT):
  696.  
  697.         /* update the pointer to the screen and then update the position
  698.          * of the physical cursor; we must use temp to get the up_row
  699.          * position and then assign it to node_row because we can't have
  700.          * node_row changing before we use it to get node_col
  701.          */
  702.  
  703.         temp =  (short) nodes[node_row][node_col].left_row;
  704.         node_col = (short) nodes[node_row][node_col].left_col;
  705.         node_row = temp;
  706.         scr_row = (int) nodes[node_row][node_col].row;
  707.         scr_col = (int) nodes[node_row][node_col].column;
  708.  
  709.         cursor_flag++;        /* reset the cursor down below          */
  710.         break;
  711.  
  712.      case(LINEFEED):        /**************************************/
  713.      case('J'):            /*             down-arrow          */
  714.      case('j'):            /**************************************/
  715.      case(KEY_DOWN):
  716.  
  717.         /* update the pointer to the screen and then update the position
  718.          * of the physical cursor; we must use temp to get the up_row
  719.          * position and then assign it to node_row because we can't have
  720.          * node_row changing before we use it to get node_col
  721.          */
  722.  
  723.         temp = (short) nodes[node_row][node_col].down_row;
  724.         node_col = (short) nodes[node_row][node_col].down_col;
  725.         node_row = temp;
  726.         scr_row = (int) nodes[node_row][node_col].row;
  727.         scr_col = (int) nodes[node_row][node_col].column;
  728.  
  729.         cursor_flag++;        /* reset the cursor down below          */
  730.         break;
  731.  
  732.      case('^'):            /**************************************/
  733.      case('K'):            /*              up-arrow          */
  734.      case('k'):            /**************************************/
  735.      case(KEY_UP):
  736.  
  737.         /* update the pointer to the screen and then update the position
  738.          * of the physical cursor; we must use temp to get the up_row
  739.          * position and then assign it to node_row because we can't have
  740.          * node_row changing before we use it to get node_col
  741.          */
  742.  
  743.         temp = (short) nodes[node_row][node_col].up_row;
  744.         node_col = (short) nodes[node_row][node_col].up_col;
  745.         node_row = temp;
  746.         scr_row = (int) nodes[node_row][node_col].row;
  747.         scr_col = (int) nodes[node_row][node_col].column;
  748.  
  749.         cursor_flag++;        /* reset the cursor down below          */
  750.         break;
  751.  
  752.      case(CARRIAGE_RETURN):        /**************************************/
  753.                     /*          carriage return          */
  754.                     /**************************************/
  755.  
  756.         /* go to left of screen and down one row on the screen */
  757.  
  758.         node_row = nodes[node_row][0].down_row;
  759.         node_col = 0;
  760.         scr_row = (int) nodes[node_row][node_col].row;
  761.         scr_col = (int) nodes[node_row][node_col].column;
  762.  
  763.         cursor_flag++;        /* reset the cursor down below          */
  764.         break;
  765.  
  766.      case('='):            /**************************************/
  767.      case('+'):            /*            plus-screen          */
  768.      case(CONTROL_F):        /**************************************/
  769.      case(KEY_NPAGE):
  770.  
  771.         if(num_screen > 1)        /* more than one screen?          */
  772.         {
  773.            curr_screen++;        /* go to next screen              */
  774.            if(curr_screen > num_screen)
  775.           curr_screen = 1;    /* oops, I meant the first screen     */
  776.  
  777.            if((curr_screen == 1) || (curr_screen == num_screen))
  778.            {
  779.           set_nodes(nodes,node_row_max,&scr_file,num_screen,
  780.                 curr_screen,num_file,slot_width,num_col); 
  781.            }
  782.  
  783.            /* update the page number in the corner */
  784.  
  785.            put_pagnum(spec_win,curr_screen,num_screen);
  786.  
  787.            /* write the files to the screen */
  788.  
  789.            make_screen(main_win,nodes,dirptr,&args,curr_screen,
  790.                node_row_max,node_col_max,scr_file,num_slot,
  791.                slot_width,text_flag);
  792.  
  793.            /* reset screen and node pointers */
  794.  
  795.            node_row = 0;
  796.            node_col = 0;
  797.            scr_row = (int) nodes[0][0].row;
  798.            scr_col = (int) nodes[0][0].column;
  799.  
  800.            /* home the cursor */
  801.  
  802.            cursor_flag++;        /* reset the cursor down below          */
  803.         }
  804.  
  805.         break;
  806.  
  807.      case('-'):            /**************************************/
  808.      case('_'):            /*            minus-screen          */
  809.      case(CONTROL_B):        /**************************************/
  810.      case(KEY_PPAGE):
  811.                            
  812.         /* reset the screen nodes if we have to */
  813.  
  814.         if(num_screen > 1)
  815.         {
  816.            curr_screen--;        /* go to previous screen           */
  817.            if(curr_screen == 0)    /* were we on the first screen?          */
  818.           curr_screen = num_screen; /* oops, go to last screen          */
  819.  
  820.            if(curr_screen >= (num_screen-1))
  821.            {
  822.                set_nodes(nodes,node_row_max,&scr_file,num_screen,
  823.                 curr_screen,num_file,slot_width,num_col); 
  824.            }
  825.  
  826.            /* update the page number in the corner */
  827.  
  828.            put_pagnum(spec_win,curr_screen,num_screen);
  829.  
  830.            /* write the files to the screen */
  831.  
  832.            make_screen(main_win,nodes,dirptr,&args,curr_screen,
  833.                node_row_max,node_col_max,scr_file,num_slot,
  834.                slot_width,text_flag);
  835.  
  836.            /* reset screen and node pointers */
  837.  
  838.            node_row = 0;
  839.            node_col = 0;                         
  840.            scr_row = (int) nodes[0][0].row;
  841.            scr_col = (int) nodes[0][0].column;
  842.  
  843.            /* home the cursor */
  844.  
  845.            cursor_flag++;        /* reset the cursor down below          */
  846.         }
  847.  
  848.         break;
  849.  
  850.      case('/'):            /**************************************/
  851.                     /*            Locate-file          */
  852.                     /**************************************/
  853.  
  854.         /* user wants to search for a filename; go for it */
  855.  
  856.         temp = (short) file_locate(main_win,dirptr,num_file);
  857.  
  858.         if(temp >= 0)
  859.         {
  860.            /* file was found; recompute the node indexes so that we
  861.         * can change the cursor on the screen
  862.         */
  863.  
  864.            new_screen = (temp / num_slot) + 1;
  865.  
  866.            /* get us to the current screen */
  867.  
  868.            temp2 = temp - ((new_screen - 1) * num_slot);
  869.            node_col = temp2 / num_row;
  870.            node_row = temp % num_row;
  871.  
  872.            /* do we need to go to a new screen?  if so, make the new
  873.         * one now
  874.         */
  875.  
  876.            if(new_screen != curr_screen)
  877.            {
  878.           /* create the new screen by first putting the new page
  879.            * number in the upper-right and then rebuilding the
  880.            * main display
  881.            */
  882.  
  883.                  /* do we need to update the screen node pointers? */
  884.  
  885.           if((new_screen == num_screen) ||
  886.              (curr_screen == num_screen))
  887.           {
  888.              /* set the nodes */
  889.                                                       
  890.              set_nodes(nodes,node_row_max,&scr_file,num_screen,
  891.                    new_screen,num_file,slot_width,num_col);
  892.           }
  893.  
  894.           curr_screen = new_screen;
  895.  
  896.             /* put the new page number on the screen */
  897.  
  898.           put_pagnum(spec_win,curr_screen,num_screen);
  899.  
  900.           /* build the display for the new screen */
  901.  
  902.           make_screen(main_win,nodes,dirptr,&args,curr_screen,
  903.                   node_row_max,node_col_max,scr_file,num_slot,
  904.                   slot_width,text_flag);
  905.            }
  906.  
  907.            /* make the cursor point to the file that was found */
  908.  
  909.            scr_row = (int) nodes[node_row][node_col].row;
  910.            scr_col = (int) nodes[node_row][node_col].column;
  911.         }
  912.         else if(temp == -1)
  913.         {
  914.            /* no file found; just put up a message and that's it */
  915.  
  916.            beep();
  917.            info_mess("File not found");
  918.            mess_flag++;
  919.         }
  920.  
  921.         /* set the cursor where it is supposed to be, whether on the
  922.          * file that was found or the file that it was on when the
  923.          * failed search request was initiated
  924.          */
  925.  
  926.         cursor_flag++;        /* reset the cursor down below          */
  927.         break;
  928.  
  929.      case('U'):            /**************************************/
  930.      case('u'):            /*              Unmark              */
  931.                       /**************************************/
  932.  
  933.         /* first get the entry pointer for the file selected */
  934.                       
  935.         entptr = dirptr + ((curr_screen - 1) * (num_row * num_col)) +
  936.              (node_col * num_row) + node_row;
  937.  
  938.         /* now cancel all commands for the file entry and free up the
  939.          * command structure that was allocated for it
  940.          */
  941.  
  942.         mark_cancel(entptr);
  943.  
  944.         /* remake the slot so it can be written */
  945.  
  946.         temp = make_slot(slot_buf,entptr,&args,slot_width,text_flag);
  947.  
  948.         /* now write the file information to the screen in the
  949.          * right spot
  950.          */
  951.  
  952.         put_slot(main_win,scr_row,scr_col+1,slot_buf,A_NORMAL);
  953.  
  954.         if(args.auto_feed == TRUE)
  955.         {
  956.            /* move to the next logical file on the screen; basically, we are
  957.         * just performing the same stuff we do for a down-arrow
  958.         */
  959.  
  960.            temp = (short) nodes[node_row][node_col].down_row;
  961.            node_col = (short) nodes[node_row][node_col].down_col;
  962.            node_row = temp;
  963.            scr_row = (int) nodes[node_row][node_col].row;
  964.            scr_col = (int) nodes[node_row][node_col].column;
  965.         }
  966.  
  967.         cursor_flag++;
  968.         break;
  969.  
  970.      case('S'):            /**************************************/
  971.      case('s'):            /*               Select              */
  972.                     /**************************************/
  973.      case('B'):            /*               Branch              */
  974.      case('b'):            /**************************************/
  975.  
  976.         change_dir = FALSE;        /* just to be safe              */
  977.  
  978.         entptr = dirptr + ((curr_screen - 1) * (num_row * num_col)) +
  979.              (node_col * num_row) + node_row;
  980.  
  981.         /* if the current file is a symbolic link, try to find out what
  982.          * it is pointing to in case we need it later
  983.          */
  984.  
  985.         if(entptr->type == LINK)
  986.            curr_type = follow_link(entptr->filename);
  987.         else
  988.            curr_type = entptr->type; /* just use what we already have     */
  989.  
  990.         if(term_code == 'S' || term_code == 's')
  991.         {
  992.            /* now see what was Selected */
  993.  
  994.            if(entptr->type == REGULAR || curr_type == REGULAR)
  995.            {
  996.           /* Selecting a file; just call the PAGER */
  997.  
  998.           if(access(entptr->filename,R_OK) != 0)
  999.               status = CANT_OPEN;
  1000.           else if(entptr->size != 0)
  1001.           {
  1002.              /* the file is not empty; go ahead and try to look at it */
  1003.  
  1004.              status = file_select(entptr->filename);
  1005.              touchwin(main_win);
  1006.              touchwin(spec_win);
  1007.              touchwin(stat_win);
  1008.              wrefresh(main_win);
  1009.              wrefresh(spec_win);
  1010.              wrefresh(stat_win);
  1011.           }
  1012.           else
  1013.              status = EMPTY_FILE; /* file is empty              */
  1014.  
  1015.           switch(status)    /* what happened with the file?          */
  1016.           {
  1017.              case(FAILURE):
  1018.  
  1019.             beep();
  1020.             info_mess("Cannot display selected file");
  1021.             mess_flag++;
  1022.             break;
  1023.  
  1024.              case(EMPTY_FILE):
  1025.  
  1026.             beep();
  1027.             info_mess("Selected file is empty");
  1028.             mess_flag++;
  1029.             break;
  1030.  
  1031.              case(CANT_OPEN):
  1032.  
  1033.             beep();
  1034.             info_mess("Cannot open file");
  1035.             mess_flag++;
  1036.             break;
  1037.  
  1038.              default:                      
  1039.             break;
  1040.           }
  1041.            }
  1042.            else if(entptr->type == DIRECTORY || curr_type == DIRECTORY)
  1043.            {
  1044.           /* Selected a directory; first see if we can even change
  1045.            * to it
  1046.            */
  1047.  
  1048.           if(chdir(entptr->filename) != 0)
  1049.           {
  1050.              beep();
  1051.              info_mess("Cannot change to selected directory");
  1052.              mess_flag++;
  1053.              cursor_flag++;
  1054.           }
  1055.           else
  1056.           {
  1057.              if(term_code == 'b') /* ask for confirmation first          */
  1058.              {
  1059.             info_mess("There are commands in this directory.  Execute them before branching? [y] ");
  1060.  
  1061.             term_code = wgetch(main_win);
  1062.  
  1063.             if(term_code == 'N' || term_code == 'n')
  1064.                break;
  1065.              }
  1066.  
  1067.              change_dir = TRUE;
  1068.           }
  1069.            }
  1070.         }
  1071.         else
  1072.         {
  1073.            /* must be a Branch command */
  1074.  
  1075.            prompt_getstr(main_win,"Directory: ",slot_buf,main_rows,
  1076.                  sizeof(slot_buf));
  1077.  
  1078.            if(slot_buf[0] == '\0')
  1079.            {
  1080.           /* user just hit a return */
  1081.  
  1082.           cursor_flag++;
  1083.            }
  1084.            else if(chdir(slot_buf) != 0)
  1085.            {
  1086.           beep();
  1087.           info_mess("Cannot change to specified directory");
  1088.           mess_flag++;
  1089.           cursor_flag++;
  1090.            }
  1091.            else
  1092.            {
  1093.           /* it worked; save the directory name and set the
  1094.            * flag that says we are Jumping
  1095.            */
  1096.  
  1097.           branched = TRUE;
  1098.           change_dir = TRUE;
  1099.            }
  1100.         }
  1101.  
  1102.         if(change_dir == TRUE)
  1103.         {
  1104.            /* now call ourselves and start over with the new directory */
  1105.  
  1106.            /***********************************
  1107.             *   R E C U R S I V E   C A L L   *
  1108.             ***********************************/
  1109.  
  1110.            do
  1111.            {
  1112.           temp = proc_dir();
  1113.            }
  1114.            while(temp == RECALL_PROC);
  1115.  
  1116.            baseptr = dirptr;    /* point to current directory          */
  1117.  
  1118.            /* change back to the directory that we should be in */
  1119.  
  1120.            if(chdir(dir_name) != 0)
  1121.            {
  1122.           /* problems changing to directory; scream at the user
  1123.            * and get out of here
  1124.            */
  1125.  
  1126.            beep();
  1127.            info_mess("Cannot reset directory....exiting...");
  1128.            sleep(2);
  1129.            exit(0);
  1130.            }
  1131.  
  1132.            /* see if the current directory has changed; if it has, we
  1133.         * need to rebuild it
  1134.         */
  1135.  
  1136.            if(stat(".",&statbuf) != 0)
  1137.           exit();
  1138.  
  1139.            if(stat_time < statbuf.st_ctime)
  1140.            {
  1141.           /* see if there are any commands to execute */
  1142.  
  1143.           if(check_marks(dirptr,num_file) == TRUE)
  1144.           {
  1145.              info_mess("Directory has been updated.  Execute marks before rebuilding? [Y] ");
  1146.              term_code = wgetch(main_win);
  1147.              clear_mess(main_win);
  1148.  
  1149.              if(term_code != 'N' && term_code != 'n')
  1150.             xecute(dirptr,dir_text,num_file);
  1151.              else
  1152.              {
  1153.             /* free up the memory for any command structures that
  1154.              * might have been allocated
  1155.              */
  1156.  
  1157.             free_comm(dirptr,num_file);
  1158.              }
  1159.           }
  1160.  
  1161.           info_mess("Rebuilding directory.....");
  1162.           level--;        /* decrement recursion level counter  */
  1163.  
  1164.           /* free up the memory pools first */
  1165.  
  1166.           free_pool(first_pool);
  1167.           unlink_dir_ent(dir_ent_ptr);
  1168.  
  1169.           /* now free up the memory for the directory information */
  1170.  
  1171.           free((char *) dirptr);
  1172.  
  1173.           werase(main_win);    /* erase display to be sure...          */
  1174.           return(RECALL_PROC);    /* go back to where we were called    */
  1175.            }
  1176.  
  1177.             if(temp == EMPTY_DIR)    /* did the user make a good choice?   */
  1178.            {
  1179.           /* the directory that was selected was empty */
  1180.  
  1181.           beep();
  1182.           info_mess("Directory is empty...........");
  1183.  
  1184.           if(exec_flag)
  1185.           {
  1186.              sleep(2);        /* only pause if we just executed     */
  1187.              clear_mess(main_win);
  1188.           }
  1189.           else
  1190.              mess_flag++;    /* clear it on next keystroke          */
  1191.            }
  1192.  
  1193.            /* reset some things on the screen since we are actually
  1194.         * returning from a directory
  1195.         */
  1196.                         
  1197.            if((args.text) && (!text_flag) && temp != EMPTY_DIR)
  1198.            {
  1199.           /* when we left this directory to go down, the
  1200.            * text descriptors were not included (at least not
  1201.            * not on the screen, anyway); when we were in
  1202.            * one of the descendant directories, the text
  1203.            * descriptors were selected; we must read them
  1204.            * in here; first, update the slot width on the
  1205.            * assumption that we can get the text descriptors
  1206.            * for the current directory without any trouble and
  1207.            * set the flag that signifies that we have text
  1208.            * descriptors read in for the current directory
  1209.            */
  1210.  
  1211.           if(args.text == DISPLAY_TEXT)    /* room to display descrips?  */
  1212.              slot_width += TEXT_MAX + 1; /* yes, assume we get them   */
  1213.  
  1214.           text_flag = args.text;
  1215.  
  1216.           /* erase the display to make sure we don't have any
  1217.            * garbage left over if we need to reformat the screen
  1218.            */
  1219.  
  1220.           werase(main_win);
  1221.  
  1222.           temp = read_text_des(dirptr,&curr_pool,&dir_text,
  1223.                       pool_length,num_file);
  1224.  
  1225.           if(temp == BAD_FILE)
  1226.           {
  1227.              beep();
  1228.              info_mess("Invalid format for text descriptor file");
  1229.              mess_flag++;
  1230.              text_flag = 0;
  1231.  
  1232.              if(args.text == DISPLAY_TEXT)
  1233.                 slot_width = slot_width - (TEXT_MAX + 1);
  1234.  
  1235.              sleep(1);
  1236.           }
  1237.           else if(temp == CANT_OPEN)
  1238.           {
  1239.              beep();
  1240.              info_mess("Cannot open text descriptor file");
  1241.              mess_flag++;
  1242.              text_flag = 0;
  1243.  
  1244.              if(args.text == DISPLAY_TEXT)
  1245.                 slot_width = slot_width - (TEXT_MAX + 1);
  1246.  
  1247.              sleep(1);
  1248.           }
  1249.           else if(temp == NO_FILE)
  1250.           {
  1251.              text_flag = 0;
  1252.  
  1253.              if(args.text == DISPLAY_TEXT)
  1254.                 slot_width = slot_width - (TEXT_MAX + 1);
  1255.           }
  1256.           else if(temp == 0)
  1257.           {
  1258.              /* reset the screen parameters, just in case they need
  1259.               * to be changed to reflect text descriptors
  1260.               */
  1261.  
  1262.              set_screen(&num_screen,&scr_file,num_file,slot_width,
  1263.                 node_row_max,&num_col);
  1264.              num_slot = scr_file;
  1265.           }
  1266.                }
  1267.  
  1268.            if(args.filemarks && !filemarks_flag)
  1269.            {
  1270.           /* the user has filemarks selected from a descendent
  1271.            * directory but we don't have them included in the
  1272.            * current directory; add them here
  1273.            */
  1274.  
  1275.           get_filemarks(dirptr,num_file);
  1276.           filemarks_flag = 1;
  1277.            }
  1278.  
  1279.            if(args.sort && !sort_flag)
  1280.            {
  1281.           /* the user turned on sorting in a descendent directory
  1282.            * but the directory isn't sorted; go ahead and sort now
  1283.            */
  1284.  
  1285.           sort_files(num_file - 1,args.sort);
  1286.           sort_flag = 1;
  1287.            }
  1288. /*
  1289.            if((temp == EMPTY_DIR && exec_flag) || temp != EMPTY_DIR)
  1290.            {
  1291. */
  1292.           /* we just executed commands in a directory so we must
  1293.            * rebuild stuff
  1294.            */
  1295.  
  1296.           set_nodes(nodes,node_row_max,&scr_file,num_screen,
  1297.                 curr_screen,num_file,slot_width,num_col);
  1298.  
  1299.           /* rewrite all of the stuff local to this directory */
  1300.  
  1301.           put_spec(spec_win,dir_name,home_dir,home_len,num_screen);
  1302.           put_stat(stat_win,num_block,num_file);
  1303.  
  1304.           if(args.text)     /* text descriptor to write?          */
  1305.              put_text(spec_win,dir_text);
  1306.  
  1307.           /* this is important; we have to look for the directory
  1308.            * that we were on when the last Select was done and de-
  1309.            * termine what page it is on; we might have to change the
  1310.            * current page and everything just to put the cursor back
  1311.            * in the right spot
  1312.            */
  1313.  
  1314.           screen_reset(nodes,dirptr,entptr->filename,&node_row,
  1315.                    &node_col,&scr_file,&curr_screen,node_row_max,
  1316.                    num_file,num_slot,num_screen,slot_width,
  1317.                    num_row,num_col);
  1318.  
  1319.           /* put the new page number on the screen */
  1320.  
  1321.           put_pagnum(spec_win,curr_screen,num_screen);
  1322.  
  1323.           /* create and write the screen again for the current
  1324.            * directory
  1325.            */
  1326.  
  1327.           make_screen(main_win,nodes,dirptr,&args,curr_screen,
  1328.                   node_row_max,node_col_max,scr_file,
  1329.                   num_slot,slot_width,text_flag);
  1330.  
  1331.           exec_flag = 0;    /* make sure to reset this          */
  1332.  
  1333.           /* reset the screen row and column */
  1334.  
  1335.           scr_row = (int) nodes[node_row][node_col].row;
  1336.           scr_col = (int) nodes[node_row][node_col].column;
  1337. /*
  1338.            }
  1339. */
  1340.         }
  1341.  
  1342.         exec_flag = 0;
  1343.         cursor_flag++;        /* reset the cursor down below          */
  1344.         break;
  1345.                         
  1346.      case('X'):            /**************************************/
  1347.      case('x'):            /*             eXecute              */
  1348.                     /**************************************/
  1349.  
  1350.         if(args.confirm)        /* should we ask to make sure?          */
  1351.         {
  1352.            /* the user wants to be asked if they REALLY want to execute
  1353.         * the commands for the directory
  1354.         */
  1355.  
  1356.            info_mess("REALLY eXecute commands? [N] ");
  1357.            term_code = wgetch(main_win);
  1358.  
  1359.            /* now look at what the keystroke is; if it is not Y or y,
  1360.         * then we DO NOT execute the commands for the directory
  1361.             */
  1362.  
  1363.            if((term_code != 'Y') && (term_code != 'y'))
  1364.            {
  1365.           /* clear out the prompt message first and then set the
  1366.            * flag to reset the cursor
  1367.            */
  1368.  
  1369.           clear_mess(main_win);
  1370.           cursor_flag++;    /* reset the cursor              */
  1371.           break;        /* nope, don't do anything yet.....   */
  1372.            }
  1373.         }
  1374.  
  1375.         /* execute the file commands for all of the files in the current
  1376.          * directory; informational messages will appear for each action
  1377.          * for each file
  1378.          */
  1379.  
  1380.         xecute(dirptr,dir_text,num_file);
  1381.         exec_flag++;        /* set flag saying we just executed   */
  1382.  
  1383.         /* just fall through here and do what we would normally do for
  1384.          * a REBUILD command; no sense in putting the code in two places,
  1385.          * now is there?
  1386.          */
  1387.  
  1388.      case(CONTROL_R):        /**************************************/
  1389.                     /*     rebuild the directory          */
  1390.                     /**************************************/
  1391.  
  1392.         /* if we are really just rebuilding, free up any command
  1393.          * structures for the directory
  1394.          */
  1395.  
  1396.         if(!exec_flag)
  1397.            free_comm(dirptr,num_file);
  1398.  
  1399.         /* tell the user we are doing something so they don't worry */
  1400.  
  1401.         xmess("Rebuilding directory information......",0);
  1402.  
  1403.         /* decrement the recursion level before returning */
  1404.  
  1405.         --level;
  1406.  
  1407.         /* free the memory pools first */
  1408.  
  1409.         free_pool(first_pool);
  1410.         unlink_dir_ent(dir_ent_ptr);
  1411.  
  1412.         /* now free up the memory for the directory information */
  1413.  
  1414.         free((char *) dirptr);
  1415.  
  1416.         return(RECALL_PROC);    /* force proc_dir() to be recalled    */
  1417.  
  1418.      case('?'):            /**************************************/
  1419.                     /*               Help              */
  1420.                     /**************************************/
  1421.  
  1422.         give_help(main_win);
  1423.         mess_flag++;
  1424.         cursor_flag++;
  1425.         break;
  1426.  
  1427.      case('D'):            /**************************************/
  1428.      case('d'):            /*              Delete              */
  1429.                     /**************************************/
  1430.  
  1431.         /* first get the entry pointer for the file indicated */
  1432.  
  1433.         entptr = dirptr + ((curr_screen - 1) * (num_row * num_col)) +
  1434.              (node_col * num_row) + node_row;
  1435.  
  1436.         mark_delete(entptr);    /* mark file for deletion          */
  1437.  
  1438.         temp = make_slot(slot_buf,entptr,&args,slot_width,text_flag);
  1439.  
  1440.         /* now write the file information to the screen in the right spot */
  1441.  
  1442.         put_slot(main_win,scr_row,scr_col+1,slot_buf,A_BOLD);
  1443.  
  1444.         if(args.auto_feed == TRUE)
  1445.         {
  1446.            /* move to the next logical file on the screen; basically, we are
  1447.         * just performing the same stuff we do for a down-arrow
  1448.         */
  1449.  
  1450.            temp = (short) nodes[node_row][node_col].down_row;
  1451.            node_col = (short) nodes[node_row][node_col].down_col;
  1452.            node_row = temp;
  1453.            scr_row = (int) nodes[node_row][node_col].row;
  1454.            scr_col = (int) nodes[node_row][node_col].column;
  1455.         }
  1456.  
  1457.         cursor_flag++;
  1458.         prev_ptr = entptr;        /* save in case user wants to repeat  */
  1459.         prev_command = DELETE;
  1460.         break;
  1461.  
  1462.      case('P'):            /**************************************/
  1463.      case('p'):            /*              Protect              */
  1464.                     /**************************************/
  1465.  
  1466.         /* first get the entry pointer for the file indicated */
  1467.  
  1468.         entptr = dirptr + ((curr_screen - 1) * (num_row * num_col)) +
  1469.              (node_col * num_row) + node_row;
  1470.  
  1471.         tag_file(main_win,scr_row,scr_col);
  1472.  
  1473.         /* get the new filename and mark the file to be renamed */
  1474.  
  1475.         temp = mark_protect(main_win,entptr,0,FALSE);
  1476.  
  1477.         if(temp != SUCCESS)        /* wot happen?                  */
  1478.         {
  1479.            beep();
  1480.            info_mess("Invalid protection string");
  1481.            mess_flag++;
  1482.         }
  1483.         else
  1484.         {
  1485.            /* highlite the file slot */
  1486.  
  1487.            highlite(main_win,slot_buf,entptr,&args,scr_row,scr_col+1,
  1488.             slot_width,text_flag);
  1489.            prev_ptr = entptr;    /* save in case user wants to repeat  */
  1490.            prev_command = PROTECT;
  1491.         }
  1492.  
  1493.         erase_tag(main_win,scr_row,scr_col);
  1494.         cursor_flag++;        /* reset the cursor down below          */
  1495.         break;
  1496.  
  1497.      case('F'):            /**************************************/
  1498.      case('f'):            /*              Finish              */
  1499.                     /**************************************/
  1500.  
  1501.  
  1502.         if(level == 1)        /* are we at the top?              */
  1503.         {
  1504.            /* beep and continue, ignoring the Finish command */
  1505.  
  1506.            beep();
  1507.            break;            /* yes, we can't go up any more       */
  1508.         }
  1509.  
  1510.         /* check for marked files first; if some are marked, ask the user
  1511.          * if Finishing is the right thing to do
  1512.          */
  1513.  
  1514.         if(check_marks(dirptr,num_file) == TRUE && term_code == 'f')
  1515.         {
  1516.            info_mess("There are commands in this directory.  Execute them before finishing? [y] ");
  1517.  
  1518.            term_code = wgetch(main_win);
  1519.            clear_mess(main_win);
  1520.            cursor_flag++;
  1521.  
  1522.            if(term_code != 'N' && term_code != 'n')
  1523.           xecute(dirptr,dir_text,num_file);
  1524.            else
  1525.           free_comm(dirptr,num_file);
  1526.         }
  1527.  
  1528.         /* the user is finished with the current directory; free up the
  1529.          * memory that was allocated for the directory and just return;
  1530.          * whether we return to the mainline or to a previous call to
  1531.            * proc_dir() is no matter to us at this point
  1532.          */
  1533.  
  1534.         level--;            /* decrement recursion level counter  */
  1535.  
  1536.         /* free up the memory pools first */
  1537.  
  1538.         free_pool(first_pool);
  1539.  
  1540.         /* now free up the memory for the directory information */
  1541.  
  1542.         free((char *) dirptr);
  1543.  
  1544.         /* unlink the active directory node from the list */
  1545.  
  1546.         unlink_dir_ent(dir_ent_ptr);
  1547.  
  1548.         return(0);            /* go back to where we were called    */
  1549.  
  1550.      case('E'):            /**************************************/
  1551.      case('e'):            /*               Edit              */
  1552.                     /**************************************/
  1553.  
  1554.         entptr = dirptr + ((curr_screen - 1) * (num_row * num_col)) +
  1555.              (node_col * num_row) + node_row;
  1556.  
  1557.         if(entptr->type != DIRECTORY)
  1558.         {
  1559.            /* Editing a file; fine... */
  1560.  
  1561.            if(access(entptr->filename,R_OK) != 0)
  1562.            {
  1563.           beep();
  1564.           info_mess("Cannot open file");
  1565.           mess_flag++;
  1566.            }
  1567.            else
  1568.            {
  1569.           /* the file is not empty; go ahead and try to edit it */
  1570.  
  1571.           /* subtract the number of blocks for the file from the total
  1572.            * for the directory because the size of the file might be
  1573.            * different
  1574.            */
  1575.  
  1576.           num_block -= get_bnum(entptr->filename);
  1577.  
  1578.           status = edit(entptr->filename);
  1579.           touchwin(main_win);
  1580.           touchwin(spec_win);
  1581.           touchwin(stat_win);
  1582.           wrefresh(main_win);
  1583.           wrefresh(spec_win);
  1584.           wrefresh(stat_win);
  1585.            }
  1586.  
  1587.            if(status == FAILURE)
  1588.            {
  1589.           beep();
  1590.           info_mess("Cannot edit selected file");
  1591.           mess_flag++;
  1592.            }
  1593.  
  1594.            /* rebuild the file entry for the file to reflect the new info;
  1595.         * save the command pointer and re-attach it to the entry because
  1596.         * make_ent() will wipe it out
  1597.         */
  1598.  
  1599.            comm_ptr = entptr->command;
  1600.            make_ent(entptr,entptr->filename,&num_block);
  1601.            entptr->command = comm_ptr;
  1602.            status = make_slot(slot_buf,entptr,&args,slot_width,text_flag);
  1603.            put_slot(main_win,scr_row,scr_col+1,slot_buf,status);
  1604.  
  1605.            /* update the stat line */
  1606.  
  1607.            put_stat(stat_win,num_block,num_file);
  1608.            cursor_flag++;
  1609.         }
  1610.         else
  1611.         {
  1612.            beep();
  1613.            info_mess("Cannot edit a directory");
  1614.            mess_flag++;
  1615.            cursor_flag++;
  1616.         }
  1617.  
  1618.         break;
  1619.  
  1620.      case('I'):            /**************************************/
  1621.      case('i'):            /*               Info              */
  1622.                     /**************************************/
  1623.  
  1624.         /* first get the entry pointer for the file indicated */
  1625.  
  1626.         entptr = dirptr + ((curr_screen - 1) * (num_row * num_col)) +
  1627.              (node_col * num_row) + node_row;
  1628.  
  1629.         /* display all of the information about the file on the screen */
  1630.  
  1631.         if(info(entptr,&args) != SUCCESS)
  1632.         {
  1633.            beep();
  1634.            info_mess("Cannot obtain information for file");
  1635.            mess_flag++;
  1636.         }
  1637.  
  1638.         touchwin(main_win);        /* to force it to be refreshed          */
  1639.         wnoutrefresh(main_win);
  1640.         touchwin(stat_win);
  1641.         wnoutrefresh(stat_win);
  1642.         doupdate();
  1643.         cursor_flag++;        /* reset the cursor              */
  1644.  
  1645.         break;
  1646.  
  1647.      case('R'):            /**************************************/
  1648.      case('r'):            /*              Rename              */
  1649.                     /**************************************/
  1650.  
  1651.         /* first get the entry pointer for the file indicated */
  1652.  
  1653.         entptr = dirptr + ((curr_screen - 1) * (num_row * num_col)) +
  1654.              (node_col * num_row) + node_row;
  1655.  
  1656.         tag_file(main_win,scr_row,scr_col);
  1657.  
  1658.         /* get the new filename and mark the file to be renamed */
  1659.  
  1660.         temp = mark_copy_ren(main_win,entptr,RENAME,NULL,FALSE);
  1661.  
  1662.         if(temp != SUCCESS)        /* wot happen?                  */
  1663.         {
  1664.            beep();
  1665.            info_mess("Error in trying to rename file");
  1666.            mess_flag++;
  1667.         }
  1668.         else
  1669.         {
  1670.            /* highlite the file slot */
  1671.  
  1672.            highlite(main_win,slot_buf,entptr,&args,scr_row,scr_col+1,
  1673.             slot_width,text_flag);
  1674.            prev_ptr = entptr;    /* save in case user wants to repeat  */
  1675.            prev_command = RENAME;
  1676.         }
  1677.  
  1678.         erase_tag(main_win,scr_row,scr_col);
  1679.         cursor_flag++;        /* reset the cursor down below          */
  1680.  
  1681.         break;
  1682.  
  1683.      case('C'):            /**************************************/
  1684.      case('c'):            /*               Copy               */
  1685.                     /**************************************/
  1686.  
  1687.          /* first get the entry pointer for the file indicated */
  1688.  
  1689.         entptr = dirptr + ((curr_screen - 1) * (num_row * num_col)) +
  1690.              (node_col * num_row) + node_row;
  1691.  
  1692.         if(entptr->type != DIRECTORY)
  1693.         {
  1694.            tag_file(main_win,scr_row,scr_col);
  1695.  
  1696.            /* get the new filename and mark the file to be copied */
  1697.  
  1698.            temp = mark_copy_ren(main_win,entptr,COPY,NULL,FALSE);
  1699.  
  1700.            if(temp != SUCCESS)    /* wot happen?                  */
  1701.            {
  1702.            beep();
  1703.            info_mess("Error in trying to copy file");
  1704.            mess_flag++;
  1705.            }
  1706.            else
  1707.            {
  1708.           /* highlite the file slot */
  1709.  
  1710.           highlite(main_win,slot_buf,entptr,&args,scr_row,scr_col+1,
  1711.                slot_width,text_flag);
  1712.               prev_ptr = entptr;    /* save in case user wants to repeat  */
  1713.           prev_command = COPY;
  1714.            }
  1715.  
  1716.            erase_tag(main_win,scr_row,scr_col);
  1717.         }
  1718.         else
  1719.         {
  1720.            beep();
  1721.            info_mess("Copy is invalid for that file");
  1722.            mess_flag++;
  1723.         }
  1724.  
  1725.         cursor_flag++;
  1726.         wrefresh(main_win);        /* make sure screen is up to date     */
  1727.         break;
  1728.  
  1729.      case('T'):            /**************************************/
  1730.      case('t'):            /*               Text              */
  1731.                     /**************************************/
  1732.  
  1733.          /* first save what file the cursor was pointing to */
  1734.  
  1735.         entptr = dirptr + ((curr_screen - 1) * (num_row * num_col)) +
  1736.              (node_col * num_row) + node_row;
  1737.  
  1738.         if(!text_flag)
  1739.         {
  1740.            /* were text descriptors already turned on? */
  1741.  
  1742.            if(!args.text)
  1743.            {
  1744.           /* now see what we need to set the text global run-time
  1745.            * flag to (whether we should display text descriptors
  1746.            * or not
  1747.            */
  1748.  
  1749.           save_width = slot_width;
  1750.  
  1751.           if((slot_width + TEXT_MAX + 1) <= COLS)
  1752.           {
  1753.              /* Good news!  The text descriptors will fit on the
  1754.               * screen; update the current slot width but leave the
  1755.               * global slot width alone
  1756.               */                                 
  1757.  
  1758.              slot_width = slot_width + TEXT_MAX + 1;
  1759.              args.text = DISPLAY_TEXT;
  1760.           }
  1761.           else
  1762.           {
  1763.              /* no, the text descriptors will not fit on the screen;
  1764.               * but we want them read in from now on anyway
  1765.               */
  1766.  
  1767.              args.text = SLOT_OVF;
  1768.           }
  1769.            }
  1770.  
  1771.            /* text descriptors have NOT been read in for the current dir-
  1772.         * ectory; first we must see if the text descriptor file exists;
  1773.         * if it is there, we will use it; if not, we will ask if they
  1774.         * want to create one
  1775.         */
  1776.  
  1777.            text_flag = args.text;
  1778.  
  1779.            temp = read_text_des(dirptr,&curr_pool,&dir_text,pool_length,
  1780.                     num_file);
  1781.  
  1782.            if(temp == NO_FILE)
  1783.            {
  1784.           /* the text descriptor file does not exist; ask the user if
  1785.            * it should be created
  1786.            */
  1787.  
  1788.           temp = create_text(main_win);
  1789.            }
  1790.  
  1791.            switch(temp)        /* what happened with the file?          */
  1792.            {
  1793.           case(NEW_FILE):    /* new text descriptor file?          */
  1794.  
  1795.              /* ask the user if the commands should be executed
  1796.               * before rebuilding things
  1797.               */
  1798.  
  1799.              if(check_marks(dirptr,num_file) == TRUE)
  1800.              {
  1801.             info_mess("Commands will be wiped out.  eXecute them?\
  1802.  [y] ");
  1803.  
  1804.             term_code = wgetch(main_win);
  1805.             clear_mess(main_win);
  1806.  
  1807.             if(term_code != 'N' && term_code != 'n')
  1808.             {
  1809.                /* execute the stuff first */
  1810.  
  1811.                /* make sure we don't try to create a text descriptor
  1812.                 * file before eXecuting the commands
  1813.                 */                                 
  1814.  
  1815.                xecute(dirptr,dir_text,num_file);
  1816.             }
  1817.             else
  1818.                free_comm(dirptr,num_file);
  1819.              }
  1820.  
  1821.              /* free up the memory pools first */
  1822.  
  1823.              free_pool(first_pool);
  1824.              unlink_dir_ent(dir_ent_ptr);
  1825.  
  1826.              /* now free up the memory for the directory info */
  1827.  
  1828.              free((char *) dirptr);
  1829.  
  1830.              --level;        /* don't count this level anymore     */
  1831.  
  1832.              /* return here and force the routine to be called
  1833.               * again; this makes the function epilogue (don't
  1834.               * you just love those programming language terms?)
  1835.               * do all of the work by allocating and initializing
  1836.               * all of the automatic variables; this is a neat
  1837.               * way to take advantage of the recursive structure
  1838.               * we have set up for ourselves because all of the
  1839.               * code to reinitialize a directory does not have
  1840.               * to be duplicated
  1841.               */
  1842.  
  1843.              return(RECALL_PROC);
  1844.  
  1845.           case(SUCCESS):    /* text descriptors read in ok?          */
  1846.  
  1847.              /* the text descriptors were read in ok; now rebuild the
  1848.               * nodes for the directory and remake the screen
  1849.               */
  1850.  
  1851.              text_flag = args.text;
  1852.              set_screen(&num_screen,&scr_file,num_file,slot_width,
  1853.                 node_row_max,&num_col);
  1854.              num_slot = scr_file;
  1855.              set_nodes(nodes,node_row_max,&scr_file,num_screen,
  1856.                    curr_screen,num_file,slot_width,num_col);
  1857.              put_spec(spec_win,dir_name,home_dir,home_len,num_screen);
  1858.              put_text(spec_win,dir_text);
  1859.  
  1860.              /* we have to look for the file that was being point to
  1861.               * when the Text command was done and determine what page
  1862.               * it is on; we might have to change the current page and
  1863.               * everything just to put the cursor back in the right spot
  1864.               */
  1865.  
  1866.              screen_reset(nodes,dirptr,entptr->filename,&node_row,
  1867.                   &node_col,&scr_file,&curr_screen,
  1868.                   node_row_max,num_file,num_slot,num_screen,
  1869.                   slot_width,num_row,num_col);
  1870.  
  1871.              /* put the page number on the screen again, just in case
  1872.               * it might have changed
  1873.               */
  1874.  
  1875.              put_pagnum(spec_win,curr_screen,num_screen);
  1876.  
  1877.              /* clear the screen to make sure we don't have any
  1878.               * any garbage left over when we put the text des-
  1879.               * criptors on the screen
  1880.               */
  1881.  
  1882.              werase(main_win);
  1883.  
  1884.              /* remake the virtual display now that it is clear */
  1885.  
  1886.              make_screen(main_win,nodes,dirptr,&args,curr_screen,
  1887.                  node_row_max,node_col_max,scr_file,
  1888.                  num_slot,slot_width,text_flag);
  1889.  
  1890.              /* reset the screen cursor values */
  1891.  
  1892.              scr_row = (int) nodes[node_row][node_col].row;
  1893.              scr_col = (int) nodes[node_row][node_col].column;
  1894.  
  1895.              /* make the sure pool length is set properly */
  1896.  
  1897.              pool_length = (num_file + 1) * ent_factor;
  1898.              cursor_flag++;    /* reset the cursor              */
  1899.              break;
  1900.  
  1901.           case(BAD_FILE):
  1902.  
  1903.              /* bad format for text descriptor file */
  1904.  
  1905.              info_mess("Invalid format for text descriptor file");
  1906.              beep();
  1907.  
  1908.              /* reset the slot width */
  1909.  
  1910.              slot_width = save_width;
  1911.              text_flag = 0;
  1912.              mess_flag++;
  1913.              break;
  1914.  
  1915.           case(CANT_OPEN):
  1916.  
  1917.              beep();
  1918.              info_mess("Cannot open text descriptor file");
  1919.  
  1920.              /* reset the slot width */
  1921.  
  1922.              slot_width = save_width;
  1923.              text_flag = 0;
  1924.              mess_flag++;
  1925.              break;
  1926.  
  1927.           case(DONT_CREATE):
  1928.           default:
  1929.  
  1930.              /* reset the slot width */
  1931.  
  1932.              slot_width = save_width;
  1933.              text_flag = 0;
  1934.              break;
  1935.            }
  1936.         }
  1937.         else
  1938.         {
  1939.            /* the text descriptors have already been read in; get the
  1940.         * text descriptor for the selected file and store it in a
  1941.         * memory pool
  1942.         */
  1943.  
  1944.            /* first get the entry pointer for the file indicated */
  1945.  
  1946.            entptr = dirptr + ((curr_screen - 1) * (num_row * num_col)) +
  1947.             (node_col * num_row) + node_row;
  1948.  
  1949.            tag_file(main_win,scr_row,scr_col);
  1950.  
  1951.            /* get the text descriptor and mark the file */
  1952.  
  1953.            temp = mark_text(main_win,entptr,NULL,FALSE);
  1954.  
  1955.            if(temp != SUCCESS)    /* did everything go ok?          */
  1956.            {
  1957.           beep();
  1958.           info_mess("Invalid text descriptor");
  1959.           mess_flag++;
  1960.            }
  1961.            else
  1962.            {
  1963.           /* highlite the file slot */
  1964.  
  1965.           highlite(main_win,slot_buf,entptr,&args,scr_row,scr_col+1,
  1966.                slot_width,text_flag);
  1967.               prev_ptr = entptr;    /* save in case user wants to repeat  */
  1968.           prev_command = TEXT;
  1969.            }
  1970.  
  1971.            erase_tag(main_win,scr_row,scr_col);
  1972.         }
  1973.  
  1974.         cursor_flag++;        /* reset the cursor down below          */
  1975.         break;
  1976.  
  1977.      case('.'):            /**************************************/
  1978.                     /*            Repeat              */
  1979.                      /**************************************/
  1980.  
  1981.         entptr = dirptr + ((curr_screen - 1) * (num_row * num_col)) +
  1982.              (node_col * num_row) + node_row;
  1983.  
  1984.         if(prev_ptr != NULL)
  1985.         {
  1986.            mark_repeat(main_win,entptr,prev_ptr,prev_command);
  1987.            highlite(main_win,slot_buf,entptr,&args,scr_row,scr_col+1,
  1988.             slot_width,text_flag);
  1989.         }
  1990.         else
  1991.         {
  1992.            beep();
  1993.            info_mess("No previous command");
  1994.            mess_flag++;
  1995.         }
  1996.  
  1997.         cursor_flag++;
  1998.         break;
  1999.  
  2000.      case('Q'):            /**************************************/
  2001.      case('q'):            /*               Quit              */
  2002.                      /**************************************/
  2003.  
  2004.         /* check for marked files first; if some are marked, ask the user
  2005.          * if Quitting is the right thing to do
  2006.          */
  2007.  
  2008.         for(temp = 0,status = FALSE; temp < level && status == FALSE;
  2009.         temp++)
  2010.            status = check_marks(dirmem[temp].ptr,dirmem[temp].num_file);
  2011.  
  2012.         if(status == TRUE && term_code == 'q')
  2013.         {
  2014.            info_mess("There are commands in this directory.  Execute them before quitting? [y] ");
  2015.  
  2016.            term_code = wgetch(main_win);
  2017.            clear_mess(main_win);
  2018.            cursor_flag++;
  2019.  
  2020.            if(term_code != 'N' && term_code != 'n')
  2021.           xecute(dirptr,dir_text,num_file);
  2022.         }
  2023.  
  2024.         clean_up();
  2025.         exit(0);            /* now we can exit              */
  2026.  
  2027.         break;
  2028.                                     
  2029.        case('!'):            /**************************************/
  2030.                     /*           Shell escape          */
  2031.                      /**************************************/
  2032.  
  2033.         /* clear out the options line so we can jump to the shell
  2034.          * a little cleaner
  2035.          */
  2036.  
  2037.         wmove(stat_win,STAT_WINDOW_ROWS - 1,0);
  2038.         wclrtoeol(stat_win);
  2039.         wrefresh(stat_win);
  2040.  
  2041.         if(spawn() == FAILURE)    /* fork the shell              */
  2042.            info_mess("Cannot fork");
  2043.  
  2044.         /* refresh the screen */
  2045.  
  2046.         put_options(stat_win);
  2047.         touchwin(main_win);
  2048.         touchwin(spec_win);
  2049.         wrefresh(main_win);
  2050.         wrefresh(spec_win);
  2051.         wrefresh(stat_win);
  2052.         cursor_flag++;
  2053.         break;
  2054.  
  2055.      case(CONTROL_W):        /**************************************/
  2056.      case(CONTROL_L):        /*           repaint screen          */
  2057.                     /**************************************/
  2058.  
  2059.         touchwin(spec_win);
  2060.         touchwin(main_win);
  2061.         touchwin(stat_win);
  2062.         wnoutrefresh(spec_win);
  2063.         wnoutrefresh(main_win);
  2064.         wnoutrefresh(stat_win);
  2065.         doupdate();
  2066.         cursor_flag++;
  2067.         break;
  2068.  
  2069.      case(CONTROL_G):        /**************************************/
  2070.                     /*                Goto              */
  2071.                     /**************************************/
  2072.  
  2073.         /* ask for a new screen number */
  2074.  
  2075.         temp = get_scr_num(main_win,curr_screen,num_screen);
  2076.  
  2077.         if(temp == BAD_SCREEN_NO)    /* was it a bad screen number?          */
  2078.         {
  2079.            beep();
  2080.            info_mess("Invalid page number");
  2081.            mess_flag++;
  2082.         }
  2083.         else
  2084.         {
  2085.            /* was the page number specified different from the
  2086.         * current one?  If not, don't do anything
  2087.         */
  2088.  
  2089.            if(curr_screen != temp)
  2090.            {
  2091.           /* the page number specified was valid; set up the new page */
  2092.  
  2093.           curr_screen = temp;    /* store the new current screen          */
  2094.  
  2095.           /* update the screen nodes array */
  2096.  
  2097.           set_nodes(nodes,node_row_max,&scr_file,num_screen,
  2098.                 curr_screen,num_file,slot_width,num_col);
  2099.  
  2100.           /* update the page number in the corner */
  2101.  
  2102.           put_pagnum(spec_win,curr_screen,num_screen);
  2103.  
  2104.           /* remake the screen to reflect the new screen specified */
  2105.  
  2106.           make_screen(main_win,nodes,dirptr,&args,curr_screen,
  2107.                   node_row_max,node_col_max,scr_file,num_slot,
  2108.                   slot_width,text_flag);
  2109.  
  2110.           /* reset the screen and node pointers */
  2111.  
  2112.           node_row = 0;
  2113.           node_col = 0;
  2114.           scr_row = (int) nodes[0][0].row;
  2115.           scr_col = (int) nodes[0][0].column;
  2116.            }
  2117.         }
  2118.  
  2119.         cursor_flag++;        /* set the cursor              */
  2120.         break;
  2121.  
  2122.      case('o'):            /**************************************/
  2123.                     /*             Owner              */
  2124.                     /**************************************/
  2125.  
  2126. #if !defined(SYSV) || defined(sun)
  2127.         if(geteuid() != 0)
  2128.         {
  2129.            beep();
  2130.            info_mess("Only the superuser can use the Owner command");
  2131.            mess_flag++;
  2132.            cursor_flag++;
  2133.            break;
  2134.         }
  2135. #endif
  2136.  
  2137.         /* change the owner of a file */
  2138.  
  2139.         entptr = dirptr + ((curr_screen - 1) * (num_row * num_col)) +
  2140.              (node_col * num_row) + node_row;
  2141.  
  2142.         tag_file(main_win,scr_row,scr_col);
  2143.  
  2144.         temp = mark_owner(main_win,entptr,0,FALSE);
  2145.  
  2146.         if(temp == SUCCESS)
  2147.         {
  2148.            /* highlite the file slot */
  2149.  
  2150.            highlite(main_win,slot_buf,entptr,&args,scr_row,scr_col+1,
  2151.                 slot_width,text_flag);
  2152.            prev_ptr = entptr;    /* save in case user wants to repeat  */
  2153.            prev_command = OWNER;
  2154.         }
  2155.         else if(temp == BAD_OWNER)
  2156.         {
  2157.                info_mess("Invalid owner specified");
  2158.            beep();
  2159.            mess_flag++;
  2160.         }
  2161.  
  2162.         erase_tag(main_win,scr_row,scr_col);
  2163.         cursor_flag++;        /* reset the cursor down below          */
  2164.         break;
  2165.  
  2166.  
  2167.      case('G'):            /**************************************/
  2168.      case('g'):            /*             Group              */
  2169.                     /**************************************/
  2170.  
  2171.         /* change the group of a file */
  2172.  
  2173.         entptr = dirptr + ((curr_screen - 1) * (num_row * num_col)) +
  2174.              (node_col * num_row) + node_row;
  2175.  
  2176.         tag_file(main_win,scr_row,scr_col);
  2177.  
  2178.         temp = mark_group(main_win,entptr,0,FALSE);
  2179.  
  2180.         if(temp == SUCCESS)
  2181.         {
  2182.            /* highlite the file slot */
  2183.  
  2184.            highlite(main_win,slot_buf,entptr,&args,scr_row,scr_col+1,
  2185.                 slot_width,text_flag);
  2186.            prev_ptr = entptr;    /* save in case user wants to repeat  */
  2187.            prev_command = GROUP;
  2188.         }
  2189.         else if(temp == BAD_GROUP)
  2190.         {
  2191.                info_mess("Invalid group specified");
  2192.            beep();
  2193.            mess_flag++;
  2194.         }
  2195.  
  2196.         erase_tag(main_win,scr_row,scr_col);
  2197.         cursor_flag++;        /* reset the cursor down below          */
  2198.         break;
  2199.  
  2200.      case('O'):            /**************************************/
  2201.                     /*           Options              */
  2202.                     /**************************************/
  2203.  
  2204.         /* first get the entry pointer for the current file */
  2205.                       
  2206.         entptr = dirptr + ((curr_screen - 1) * (num_row * num_col)) +
  2207.              (node_col * num_row) + node_row;
  2208.  
  2209.         temp = config_options(&args);
  2210.  
  2211.         /* reinitialize the global factors */
  2212.  
  2213.         set_args(&args,&def_slot_wid,&ent_factor);
  2214.  
  2215.         /* slot width has probably changed */
  2216.  
  2217.         slot_width = def_slot_wid;
  2218.  
  2219.         if(temp & REBUILD_DIRECTORY)
  2220.         {
  2221.            /* ask the user if the commands should be executed
  2222.         * before rebuilding things
  2223.         */
  2224.  
  2225.            if(check_marks(dirptr,num_file) == TRUE)
  2226.            {
  2227.           touchwin(main_win);
  2228.           wnoutrefresh(main_win);
  2229.           doupdate();
  2230.           info_mess("Commands will be wiped out.  eXecute them? [y] ");
  2231.           term_code = wgetch(main_win);
  2232.           clear_mess(main_win);
  2233.  
  2234.           if(term_code != 'N' && term_code != 'n')
  2235.           {
  2236.              /* execute the stuff first */
  2237.  
  2238.              xecute(dirptr,dir_text,num_file);
  2239.           }
  2240.           else
  2241.              free_comm(dirptr,num_file);
  2242.            }
  2243.  
  2244.            /* refresh the stat window because we overwrote it */
  2245.  
  2246.            touchwin(stat_win);
  2247.            wrefresh(stat_win);
  2248.  
  2249.            /* free up the memory pools first */
  2250.  
  2251.            free_pool(first_pool);
  2252.            unlink_dir_ent(dir_ent_ptr);
  2253.  
  2254.            /* now free up the memory for the directory info */
  2255.  
  2256.            free((char *) dirptr);
  2257.  
  2258.            --level;            /* don't count this level anymore     */
  2259.  
  2260.            return(RECALL_PROC);    /* recall proc_dir() to rebuild       */
  2261.         }
  2262.  
  2263.         if(temp & REBUILD_SCREEN)
  2264.         {
  2265.            if(temp & REBUILD_WITH_FILEMARKS)
  2266.            {
  2267.           get_filemarks(dirptr,num_file);
  2268.           filemarks_flag = args.filemarks;
  2269.            }
  2270.  
  2271.            if(temp & REBUILD_WITH_SORT)
  2272.            {
  2273.           tptr = entptr->filename;
  2274.           sort_files(num_file - 1,args.sort);
  2275.           sort_flag = args.sort;
  2276.            }
  2277.  
  2278.            if(text_flag && !args.text)
  2279.            {
  2280.           /* text descriptors were turned off; make sure that we
  2281.            * wipe out any directory text descriptor
  2282.            */
  2283.  
  2284.           put_text(spec_win,NULL);
  2285.           text_flag = 0;
  2286.            }
  2287.  
  2288.            get_text(&args,dirptr,&curr_pool,&dir_text,&mess_flag,
  2289.             &slot_width,&text_flag,pool_length,num_file);
  2290.  
  2291.            /* reinitialize the screen parameters */
  2292.  
  2293.            set_screen(&num_screen,&scr_file,num_file,slot_width,
  2294.               node_row_max,&num_col);
  2295.  
  2296.            num_slot = scr_file;    /* save number of slots per screen    */
  2297.  
  2298.            /* search for the filename of the file we were on so we can
  2299.         * go back to it; we might as well do this every time, es-
  2300.         * pecially since the search for it is fast (we're only
  2301.         * comparing pointer values instead of strings
  2302.         */
  2303.  
  2304.            temp = (short) file_ptr_search(dirptr,tptr,num_file);
  2305.  
  2306.            if(temp >= 0)
  2307.            {
  2308.           /* file was found; recompute the node indexes so that we
  2309.            * can change the cursor on the screen
  2310.            */
  2311.  
  2312.           new_screen = (temp / num_slot) + 1;
  2313.  
  2314.           /* get us to the current screen */
  2315.  
  2316.           temp2 = temp - ((new_screen - 1) * num_slot);
  2317.           node_col = temp2 / num_row;
  2318.           node_row = temp % num_row;
  2319.           curr_screen = new_screen;
  2320.            }
  2321.  
  2322.            /* create the new screen by first putting the new page
  2323.         * number in the upper-right and then rebuilding the
  2324.         * main display
  2325.         */
  2326.  
  2327.            put_pagnum(spec_win,curr_screen,num_screen);
  2328.  
  2329.            set_nodes(nodes,node_row_max,&scr_file,num_screen,new_screen,
  2330.              num_file,slot_width,num_col);
  2331.  
  2332.            if(args.text)        /* write text descrip for directory?  */
  2333.           put_text(spec_win,dir_text);
  2334.  
  2335.            werase(main_win);    /* make sure we start clean          */
  2336.  
  2337.            /* display the current screen */
  2338.              
  2339.            make_screen(main_win,nodes,dirptr,&args,curr_screen,
  2340.                node_row_max,node_col_max,scr_file,num_slot,
  2341.                slot_width,text_flag);
  2342.  
  2343.            /* get the cursor coordinates so we can reset it */
  2344.  
  2345.            scr_row = (int) nodes[node_row][node_col].row;
  2346.            scr_col = (int) nodes[node_row][node_col].column;
  2347.         }
  2348.  
  2349.         put_stat(stat_win,num_block,num_file);
  2350.         put_options(stat_win);
  2351.         touchwin(stat_win);
  2352.         touchwin(main_win);
  2353.         wnoutrefresh(main_win);
  2354.         wnoutrefresh(stat_win);
  2355.         doupdate();
  2356.         cursor_flag++;
  2357.         break;
  2358.  
  2359.      case(0):            /**************************************/
  2360.                           /*        something weird  :-C          */
  2361.                     /**************************************/
  2362.         break;
  2363.  
  2364.      default:            /**************************************/
  2365.                     /*            all others          */
  2366.                          /**************************************/
  2367.  
  2368.         /* scream at the user (ring the bell) */
  2369.  
  2370.         beep();
  2371.         break;
  2372.  
  2373.       } /* switch(term_code) */
  2374.  
  2375.       /* do we need reposition the cursor? */
  2376.  
  2377.       if(cursor_flag)
  2378.       {
  2379.      /* set the cursor where it should be */
  2380.  
  2381.      wmove(main_win,scr_row,scr_col);
  2382.      wrefresh(main_win);        /* make sure screen is up to date     */
  2383.      cursor_flag = 0;        /* clear the cursor set flag          */
  2384.       }
  2385.  
  2386.    } /* for(;;) */            /* end of main processing loop          */
  2387.  
  2388. }    /*** proc_dir ***/
  2389.  
  2390. /*******************************************************************************
  2391. ********************************************************************************
  2392.  
  2393.   Function:    find_dir
  2394.  
  2395.   Purpose:    Search for a directory info node in the link list.  If one
  2396.         is found, it means that the directory is currently active.
  2397.  
  2398.   Global variables:
  2399.  
  2400.     Name            Examine/Modify/Use/Read/Write
  2401.     ----            -----------------------------
  2402.     none
  2403.  
  2404.   Return Codes:
  2405.  
  2406.     Code            Reason
  2407.     ----            ------
  2408.     none
  2409.  
  2410. ********************************************************************************
  2411. *******************************************************************************/
  2412.  
  2413. static DIR_PTR *find_dir(head,tail,dir_name)
  2414.                     /*******   FORMAL  PARAMETERS   *******/
  2415.      DIR_PTR  *head,        /* pointer to head of dir info nodes  */
  2416.           *tail;        /* pointer to end of dir info nodes   */
  2417.      char      *dir_name;        /* directory name to search for          */
  2418.  
  2419. {    /*** find_dir ***/
  2420.  
  2421.    head = head->next;
  2422.  
  2423.    while(head != tail && strcmp(head->dir_name,dir_name) != 0)
  2424.       head = head->next;
  2425.  
  2426.    if(head == tail)
  2427.       return(NULL);
  2428.  
  2429.    return(head);
  2430.  
  2431. }    /*** find_dir ***/
  2432.  
  2433. /*******************************************************************************
  2434. ********************************************************************************
  2435.  
  2436.   Function:    link_dir_ent
  2437.  
  2438.   Purpose:    Link the info for the current directory into the active
  2439.         directory linked list.
  2440.  
  2441.   Global variables:
  2442.  
  2443.     Name            Examine/Modify/Use/Read/Write
  2444.     ----            -----------------------------
  2445.     none
  2446.  
  2447.   Return Codes:
  2448.  
  2449.     Code            Reason
  2450.     ----            ------
  2451.     temp_ptr        pointer to new node
  2452.     NULL            couldn't get memory for node
  2453.  
  2454. ********************************************************************************
  2455. *******************************************************************************/
  2456.  
  2457. static DIR_PTR *link_dir_ent(dir_ent_head,dir_ent_tail,dirptr,dir_name,
  2458.                  first_pool,curr_pool,pool_length,stat_time,
  2459.                  num_file)
  2460.                     /*******   FORMAL  PARAMETERS   *******/
  2461.      DIR_PTR  *dir_ent_head,    /* head of directory chain list          */
  2462.           *dir_ent_tail;    /* tail of directory chain list          */
  2463.      ENT_DEF  *dirptr;        /* pointer to directory information   */
  2464.      char      *dir_name;        /* name of current directory          */
  2465.      POOL_DEF *first_pool,        /* first memory pool for directory    */
  2466.           *curr_pool;        /* current memory pool              */
  2467.      size_t      pool_length;        /* length to allocate for new pool    */
  2468.      time_t      stat_time;        /* time directory was last modified   */
  2469.      short      num_file;        /* number of files in directory          */
  2470.  
  2471. {    /*** link_dir_ent ***/
  2472.                     /********   LOCAL  VARIABLES   ********/
  2473.      DIR_PTR  *new_ptr,        /* new node pointer              */          *temp_ptr;        /* previous node in list          */
  2474.  
  2475.  
  2476.    /* allocate the node first */
  2477.  
  2478.    new_ptr = (DIR_PTR *) malloc(sizeof(DIR_PTR));
  2479.  
  2480.    if(new_ptr == NULL)
  2481.       return(NULL);            /* couldn't get the memory          */
  2482.  
  2483.    /* got the memory for the node; link it in */
  2484.  
  2485.    temp_ptr = dir_ent_tail->prev;    /* save where we were              */
  2486.    dir_ent_tail->prev = new_ptr;
  2487.    new_ptr->next = dir_ent_tail;
  2488.    temp_ptr->next = new_ptr;
  2489.    new_ptr->prev = temp_ptr;
  2490.  
  2491.    new_ptr->dirptr = dirptr;
  2492.    new_ptr->dir_name = dir_name;
  2493.    new_ptr->first_pool = first_pool;
  2494.    new_ptr->curr_pool = curr_pool;
  2495.    new_ptr->pool_length = pool_length;
  2496.    new_ptr->num_file = num_file;
  2497.    new_ptr->stat_time = stat_time;
  2498.    new_ptr->count = 1;            /* first time here              */
  2499.  
  2500.    return(new_ptr);            /* return pointer to new node          */
  2501.  
  2502. }    /*** link_dir_ent ***/
  2503.  
  2504. /*******************************************************************************
  2505. ********************************************************************************
  2506.  
  2507.   Function:    unlink_dir_ent
  2508.  
  2509.   Purpose:    Unlink the info for the current directory from the active
  2510.         directory linked list.
  2511.  
  2512.   Global variables:
  2513.  
  2514.     Name            Examine/Modify/Use/Read/Write
  2515.     ----            -----------------------------
  2516.     none
  2517.  
  2518.   Return Codes:
  2519.  
  2520.     Code            Reason
  2521.     ----            ------
  2522.     none
  2523.  
  2524. ********************************************************************************
  2525. *******************************************************************************/
  2526.  
  2527. static void unlink_dir_ent(current)
  2528.                     /*******   FORMAL  PARAMETERS   *******/
  2529.      DIR_PTR  *current;        /* active directory list node          */
  2530.  
  2531. {    /*** unlink_dir_ent ***/
  2532.                     /********   LOCAL  VARIABLES   ********/
  2533.      DIR_PTR  *temp;        /* temporary node pointer          */
  2534.  
  2535.  
  2536.    if(current->count > 1)        /* can we unlink it?              */
  2537.    {
  2538.       current->count -= 1;
  2539.       return;                /* nope, still active              */
  2540.    }
  2541.  
  2542.    temp = current->prev;
  2543.    temp->next = current->next;
  2544.    temp = current->next;
  2545.    temp->prev = current->prev;
  2546.    free(current->dir_name);        /* free up directory name string      */
  2547.    free(current);            /* free up the node itself          */
  2548.  
  2549.    return;
  2550.  
  2551. }    /*** unlink_dir_ent ***/
  2552.  
  2553. /*******************************************************************************
  2554. ********************************************************************************
  2555.  
  2556.   Function:    get_year
  2557.  
  2558.   Purpose:    Obtain and return the value for the current year.
  2559.  
  2560.   Global variables:
  2561.  
  2562.     Name            Examine/Modify/Use/Read/Write
  2563.     ----            -----------------------------
  2564.     none
  2565.  
  2566.   Return Codes:
  2567.  
  2568.     Code            Reason
  2569.     ----            ------
  2570.     tstr->tm_year        current year value
  2571.  
  2572. ********************************************************************************
  2573. *******************************************************************************/
  2574.  
  2575. static int get_year()
  2576.  
  2577. {    /*** get_year ***/
  2578.                     /********   LOCAL  VARIABLES   ********/
  2579.      long      clock;        /* binary time value              */
  2580. struct     tm      *tstr;        /* time structure pointer          */
  2581.  
  2582.  
  2583.    clock = (long) time((long *) 0);    /* get current time              */
  2584.    tstr = localtime(&clock);        /* fill in the time structure          */
  2585.    return(tstr->tm_year);        /* return the current year value      */
  2586.  
  2587. }    /*** get_year ***/
  2588.