home *** CD-ROM | disk | FTP | other *** search
/ High Voltage Shareware / high1.zip / high1 / DIR8 / TDE32.ZIP / DIRLIST.C < prev    next >
C/C++ Source or Header  |  1993-11-13  |  60KB  |  1,802 lines

  1. /*
  2.  * I wrote this function because I'm so stupid, I constantly forget
  3.  *  file names and directory stuff.  The main function prompts for a
  4.  *  subdirectory name or a search path.  The default search path is the
  5.  *  cwd (current working directory).  In addition to being stupid, I'm also
  6.  *  lazy.  If the user types a subdirectory name, I think we can assume he
  7.  *  wants to list all files w/o having to type *.*   Let's save the cwd on
  8.  *  whatever drive the user wishes to search, so we can restore it we get
  9.  *  thru dir'ing.  Use the standard DOS functions to get and set directories.
  10.  *
  11.  * The search pattern can contain wild card chars, valid file names, or a
  12.  *  valid subdirectory name.
  13.  *
  14.  * Being that TDE 2.2 now handles binary files, lets make .EXE and .COM files
  15.  *  autoload in binary mode.
  16.  *
  17.  * Before matching files are displayed on the screen, file names are sorted
  18.  *  using the easy-to-implement and fairly fast Shellsort algorithm.
  19.  *
  20.  * See:
  21.  *
  22.  *   Donald Lewis Shell, "A High-Speed Sorting Procedure."  _Communications of
  23.  *     the ACM_ 2 (No. 2): 30-32, 1959.
  24.  *
  25.  * See also:
  26.  *
  27.  *   Donald Ervin Knuth, _The Art of Computer Programming; Volume 3:  Sorting
  28.  *     and Searching_, Addison-Wesley, Reading, Mass., 1973, Chapter 5,
  29.  *     "Sorting", pp 84-95.  ISBN 0-201-03803-X.
  30.  *
  31.  *   Robert Sedgewick, _Algorithms in C_, Addison-Wesley, Reading, Mass.,
  32.  *     1990, Chapter 8, "Elementary Sorting Methods", pp 107-111.
  33.  *     ISBN 0-201-51425-7.
  34.  *
  35.  *
  36.  * New editor name:  TDE, the Thomson-Davis Editor.
  37.  * Author:           Frank Davis
  38.  * Date:             June 5, 1991, version 1.0
  39.  * Date:             July 29, 1991, version 1.1
  40.  * Date:             October 5, 1991, version 1.2
  41.  * Date:             January 20, 1992, version 1.3
  42.  * Date:             February 17, 1992, version 1.4
  43.  * Date:             April 1, 1992, version 1.5
  44.  * Date:             June 5, 1992, version 2.0
  45.  * Date:             October 31, 1992, version 2.1
  46.  * Date:             April 1, 1993, version 2.2
  47.  * Date:             June 5, 1993, version 3.0
  48.  * Date:             August 29, 1993, version 3.1
  49.  * Date:             November 13, 1993, version 3.2
  50.  *
  51.  * This code is released into the public domain, Frank Davis.
  52.  *    You may distribute it freely.
  53.  */
  54.  
  55. #include "tdestr.h"
  56. #include "common.h"
  57. #include "define.h"
  58. #include "tdefunc.h"
  59.  
  60.  
  61. #if defined( __UNIX__ )
  62. /*
  63.  **********************************************************************
  64.  ******************************  PART 1  ******************************
  65.  **********************************************************************
  66.  *
  67.  * Let's try to make unix have the look and feel of a PC.
  68.  */
  69.  
  70.  
  71. /*
  72.  * Name:    dir_help
  73.  * Purpose: To prompt the user and list the directory contents
  74.  * Date:    November 13, 1993
  75.  * Passed:  window:  pointer to current window
  76.  */
  77. int  dir_help( TDE_WIN *window )
  78. {
  79. char dname[PATH_MAX+2]; /* directory search pattern */
  80. char stem[PATH_MAX+2];  /* directory stem */
  81. int  rc;
  82. int  file_mode;
  83. int  bin_length;
  84. int  prompt_line;
  85.  
  86.    if (window != NULL) {
  87.       entab_linebuff( );
  88.       if (un_copy_line( window->ll, window, TRUE ) == ERROR)
  89.          return( ERROR );
  90.       prompt_line = window->bottom_line;
  91.    } else
  92.       prompt_line = g_display.nlines;
  93.  
  94.    /*
  95.     * search path or pattern
  96.     */
  97.    dname[0] = '\0';
  98.    rc = get_name( dir1, prompt_line, dname, g_display.message_color );
  99.  
  100.    if (rc == OK) {
  101.       if (validate_path( dname, stem ) == OK) {
  102.          rc = list_and_pick( dname, stem, window );
  103.          if (access( dname, F_OK ) == ERROR)
  104.             rc = ERROR;
  105.  
  106.          /*
  107.           * if everything is everything, load in the file selected by user.
  108.           *  dname contains complete path to file.  stem contains file name.
  109.           */
  110.          if (rc == OK) {
  111.             if (access( dname, X_OK ) != ERROR) {
  112.                file_mode = BINARY;
  113.                bin_length = g_status.file_chunk;
  114.             } else {
  115.                file_mode = TEXT;
  116.                bin_length = 0;
  117.             }
  118.             if (window != NULL)
  119.                attempt_edit_display( dname, LOCAL, file_mode, bin_length );
  120.             else
  121.                attempt_edit_display( dname, GLOBAL, file_mode, bin_length );
  122.          }
  123.       } else
  124.          /*
  125.           * invalid path or file name
  126.           */
  127.          error( WARNING,
  128.                 window != NULL ? window->bottom_line : g_display.nlines, dir2 );
  129.    }
  130.    return( rc );
  131. }
  132.  
  133.  
  134. /*
  135.  * Name:    validate_path
  136.  * Purpose: make sure we got a valid search pattern or subdirectory
  137.  * Date:    November 13, 1993
  138.  * Passed:  dname: search path entered by user
  139.  *          stem:  directory stem is returned
  140.  * Returns: successful or not
  141.  * Notes:   we need to validate the search directory.  we only let the
  142.  *            user enter a valid directory name -- there are too damn
  143.  *            many expections and special cases in unix.
  144.  *          if the user presses enter, lets assume he wants cwd.
  145.  */
  146. int  validate_path( char *dname, char *stem )
  147. {
  148. int  rc;
  149. int  len;
  150. struct stat fstat;
  151. char temp[PATH_MAX+2];  /* directory stem */
  152.  
  153.    rc = OK;
  154.    /*
  155.     * if path name is empty then the current working directory is implied.
  156.     */
  157.    if (dname[0] == '\0') {
  158.  
  159.       if (getcwd( temp, PATH_MAX+2 ) != NULL)
  160.  
  161.          /*
  162.           * copy current working directory into stem.
  163.           */
  164.          strcpy( dname, temp );
  165.       else
  166.          rc = ERROR;
  167.    }
  168.  
  169.    if (rc == OK) {
  170.  
  171.       /*
  172.        * get the attributes of the search pattern.  unlike DOS where we
  173.        *   can do file patterns, ie. *.c, lets only do directories in unix.
  174.        * the functions below are POSIX, i think...
  175.        */
  176.       if (stat( dname, &fstat ) >= 0) {
  177.          if (!S_ISDIR( fstat.st_mode ))
  178.             rc = ERROR;
  179.       } else
  180.          rc = ERROR;
  181.    }
  182.  
  183.    if (rc == OK) {
  184.       /*
  185.        * if this is the root directory ( / ), don't append '/' to it.
  186.        */
  187.       strcpy( stem, dname );
  188.       len = strlen( stem );
  189.       if (stem[len-1] != '/')
  190.          strcat( stem, "/" );
  191.    }
  192.    return( rc );
  193. }
  194.  
  195.  
  196. /*
  197.  * Name:    list_and_pick
  198.  * Purpose: To show matching file names and let user pick a file
  199.  * Date:    November 13, 1993
  200.  * Passed:  dname:  directory search pattern
  201.  *          stem:   stem of directory search pattern (directory name with '/')
  202.  *          window:  pointer to current window
  203.  * Returns: return code from pick.  rc = OK, then edit a new file.
  204.  * Notes:   real work routine of this function.  save the cwd and let the
  205.  *           user search upwards or downwards thru the directory structure.
  206.  */
  207. int  list_and_pick( char *dname, char *stem, TDE_WIN *window )
  208. {
  209. int  rc;
  210. UNIX_DTA unix_dta;      /* our unix dta */
  211. DIR  *dp;               /* DIR is defined in <dirent.h> */
  212. struct stat fstat;      /* stat buffer */
  213. DIRECTORY dir;          /* contains all info for dir display */
  214. unsigned int cnt;       /* number of matching files */
  215. FTYPE *flist, *p;       /* pointer to list of matching files */
  216. char cwd[PATH_MAX+2];   /* save the current working directory in this buff */
  217. char dbuff[PATH_MAX+2]; /* temporary directory buff */
  218. char prefix[PATH_MAX+2];/* directory prefix  */
  219. char *eos;              /* end of stem pointer */
  220. int  change_directory = FALSE;
  221. int  stop;
  222. int  len;
  223.  
  224.    /*
  225.     * who knows the directory structure for unix systems???  it varies from
  226.     *  release to release and from port to port.  use POSIX functions, which
  227.     *  also change from release to release (IEEE 1003.1 vs IEEE 1003.1a),
  228.     *  to get directory elements.
  229.     */
  230.    rc = OK;
  231.    if ((dp = opendir( dname )) == NULL) {
  232.       rc = ERROR;
  233.       flist = NULL;
  234.    } else {
  235.  
  236.       /*
  237.        * eos == end of stem
  238.        */
  239.       strcpy( prefix, stem );
  240.       eos = prefix + strlen( prefix );
  241.  
  242.       for (cnt=1; my_findnext( dp, &unix_dta ) == OK;) {
  243.          strcpy( eos, unix_dta.fname );
  244.          if (stat( eos, &fstat ) >= 0) {
  245.             if (S_ISREG( fstat.st_mode ) || S_ISDIR( fstat.st_mode ))
  246.                ++cnt;
  247.          }
  248.       }
  249.       closedir( dp );
  250.       flist = (FTYPE *)calloc( cnt, sizeof(FTYPE) );
  251.    }
  252.  
  253.  
  254.    if (rc != ERROR && flist != NULL) {
  255.  
  256.       stop = FALSE;
  257.       if (getcwd( cwd, PATH_MAX+2 ) == NULL) {
  258.          stop = TRUE;
  259.          rc = ERROR;
  260.       }
  261.  
  262.       while (stop == FALSE) {
  263.          /*
  264.           * If we had enough memory, find all matching file names.  Append
  265.           *  '/' to the end of subdirectory names so user will know if
  266.           *  name is a directory.  Might as well find everything, because
  267.           *  i also forget subdirectory names, too.
  268.           *
  269.           * when we get here, we have already done: 1) my_findfirst and
  270.           *  my_findnext, 2) counted the number of matching files, and
  271.           *  3) allocated space.
  272.           */
  273.          p = flist;
  274.          cnt = 0;
  275.  
  276.          if ((dp = opendir( dname )) == NULL)
  277.             rc = ERROR;
  278.          if (rc != ERROR) {
  279.  
  280.             /*
  281.              * p is pointer that walks down the file info structure.
  282.              *  save the file name, file size, and directory character,
  283.              *  if needed, for each matching file we find.
  284.              */
  285.  
  286.             /*
  287.              * eos == end of stem
  288.              */
  289.             strcpy( prefix, stem );
  290.             eos = prefix + strlen( prefix );
  291.  
  292.             for (cnt=0; my_findnext( dp, &unix_dta ) == OK;) {
  293.  
  294.                assert( strlen( unix_dta.fname ) < NAME_MAX + 2 );
  295.  
  296.                strcpy( eos, unix_dta.fname );
  297.                if (stat( eos, &fstat ) >= 0) {
  298.                   if (S_ISREG( fstat.st_mode ) || S_ISDIR( fstat.st_mode )) {
  299.                      strcpy( p->fname, unix_dta.fname );
  300.                      p->fsize = fstat.st_size;
  301.                      if (S_ISDIR( fstat.st_mode ))
  302.                         strcat( p->fname, "/" );
  303.                      ++cnt;
  304.                      ++p;
  305.                   }
  306.                }
  307.             }
  308.             closedir( dp );
  309.          }
  310.  
  311.          if (rc != ERROR) {
  312.             shell_sort( flist, cnt );
  313.  
  314.             /*
  315.              * figure out number of rows, cols, etc... then display dir list
  316.              */
  317.             setup_directory_window( &dir, cnt );
  318.             write_directory_list( flist, dir );
  319.  
  320.             /*
  321.              * Let user select file name or another search directory.
  322.              *  Save the choice in dbuff.  rc == OK if user selected file or dir.
  323.              */
  324.             rc = select_file( flist, stem, &dir );
  325.  
  326.             assert( strlen( flist[dir.select].fname ) < NAME_MAX );
  327.  
  328.             strcpy( dbuff, flist[dir.select].fname );
  329.          }
  330.  
  331.          /*
  332.           *  give memory back.
  333.           */
  334.          free( flist );
  335.          flist = NULL;
  336.  
  337.          if (rc == ERROR)
  338.             stop = TRUE;
  339.          else {
  340.             len = strlen( dbuff );
  341.  
  342.             /* check this */
  343.  
  344.             /*
  345.              * If the last character in a file name is '/' then let's
  346.              *  do a dir on selected directory.  See the matching
  347.              *  else when the user selects a file.
  348.              */
  349.             if (dbuff[len-1] == '/') {
  350.  
  351.                /*
  352.                 * Stem has subdirectory path.  dbuff has selected path.
  353.                 * Create a new dname with stem and dbuff.
  354.                 */
  355.  
  356.                assert( strlen( stem ) + strlen( dbuff ) < PATH_MAX );
  357.  
  358.                strcpy( dname, stem );
  359.                strcat( dname, dbuff );
  360.                len = strlen( dname );
  361.                strcpy( dbuff, dname );
  362.  
  363.                /*
  364.                 * The last character in dbuff is '/', because we append the
  365.                 *  '/' to every directory entry in the file list.  Replace
  366.                 *  it with a NULL char then we will have a valid path name.
  367.                 */
  368.                dbuff[len-1] = '\0';
  369.  
  370.                /*
  371.                 * now let's change to the selected subdirectory.
  372.                 */
  373.                rc = set_current_directory( dbuff );
  374.                if (rc == OK) {
  375.  
  376.                   /*
  377.                    * Every time we change directories, we need to get the
  378.                    *  current directory so we will be sure to have the
  379.                    *  correct path.
  380.                    */
  381.                   rc = get_current_directory( dbuff, PATH_MAX + 2 );
  382.                   if (rc == OK) {
  383.  
  384.                      assert( strlen( dbuff ) < PATH_MAX );
  385.  
  386.                      strcpy( dname, dbuff );
  387.                      change_directory = TRUE;
  388.                   }
  389.                }
  390.  
  391.                /*
  392.                 * Validate the new path and allocate memory for the
  393.                 *  matching files.
  394.                 */
  395.                if (rc == OK)
  396.                   rc = validate_path( dname, stem );
  397.                if (rc == OK) {
  398.  
  399.                   if ((dp = opendir( dname )) == NULL) {
  400.                      rc = ERROR;
  401.                      flist = NULL;
  402.                   } else {
  403.  
  404.                      /*
  405.                       * eos == end of stem.  here, we just count the
  406.                       *  number of files and subdirectories and allocate
  407.                       *  enough memory for the flist.
  408.                       */
  409.                      strcpy( prefix, stem );
  410.                      eos = prefix + strlen( prefix );
  411.                      for (cnt=1; my_findnext( dp, &unix_dta ) == OK;) {
  412.                         strcpy( eos, unix_dta.fname );
  413.                         if (stat( eos, &fstat ) >= 0) {
  414.                            if (S_ISREG( fstat.st_mode ) || S_ISDIR( fstat.st_mode ))
  415.                               ++cnt;
  416.                         }
  417.                      }
  418.                      closedir( dp );
  419.                      flist = (FTYPE *)calloc( cnt, sizeof(FTYPE) );
  420.                   }
  421.                }
  422.                if (flist == NULL || rc == ERROR) {
  423.                   stop = TRUE;
  424.                   rc = ERROR;
  425.                   if (flist != NULL) {
  426.                      free( flist );
  427.                      flist = NULL;
  428.                   }
  429.                }
  430.             } else {
  431.  
  432.                /*
  433.                 * user selected a file.  store complete path in dname and
  434.                 *  file name in stem.
  435.                 */
  436.                rc = OK;
  437.                stop = TRUE;
  438.  
  439.                assert( strlen( stem ) + strlen( dbuff ) < PATH_MAX );
  440.  
  441.                strcpy( dname, stem );
  442.                strcat( dname, dbuff );
  443.                strcpy( stem, dbuff );
  444.             }
  445.          }
  446.       }
  447.  
  448.       /*
  449.        * Go back to the current directory if needed.
  450.        */
  451.       if (change_directory)
  452.          set_current_directory( cwd );
  453.       if (window != NULL)
  454.          redraw_screen( window );
  455.    } else {
  456.       /*
  457.        * out of memory
  458.        */
  459.       error( WARNING,  window != NULL ? window->bottom_line : g_display.nlines,
  460.              dir3 );
  461.       rc = ERROR;
  462.    }
  463.    return( rc );
  464. }
  465.  
  466.  
  467. /*
  468.  * Name:     setup_directory_window
  469.  * Purpose:  set number of rows and cols in directory window
  470.  * Date:     November 13, 1993
  471.  * Modified: November 13, 1993, Frank Davis per Byrial Jensen
  472.  * Passed:   dir: pointer to directory structure
  473.  *           cnt: number of files
  474.  * Notes:    set up stuff we need to know about how to display files.
  475.  */
  476. void setup_directory_window( DIRECTORY *dir, int cnt )
  477. {
  478. int  i;
  479. int  wid;
  480. char temp[MAX_COLS];    /* line to output */
  481.  
  482.    /*
  483.     * setup the fixed vars used in dir display.
  484.     *    dir->col =      physical upper left column of dir screen
  485.     *    dir->row =      physical upper left row or line of dir screen
  486.     *    dir->wid =      width of physical screen
  487.     *    dir->hgt =      height of physical screen
  488.     *    dir->max_cols   number of columns of files in dir screen
  489.     *    dir->max_lines  number of lines of files in each column in dir screen
  490.     *    dir->cnt        number of files in list
  491.     */
  492.    dir->col = 3;
  493.    dir->row = 5;
  494.    wid = dir->wid = 72;
  495.    dir->hgt = 16;
  496.    dir->max_cols = 4;
  497.    dir->max_lines = 10;
  498.    dir->cnt = cnt;
  499.  
  500.    /*
  501.     * Find out how many lines in each column are needed to display
  502.     *  matching files.
  503.     */
  504.    dir->lines = dir->cnt / dir->max_cols + (dir->cnt % dir->max_cols ? 1 : 0);
  505.    if (dir->lines > dir->max_lines)
  506.       dir->lines = dir->max_lines;
  507.  
  508.    /*
  509.     * Find out how many columns of file names we need.
  510.     */
  511.    dir->cols = dir->cnt / dir->lines + (dir->cnt % dir->lines ? 1 : 0);
  512.    if (dir->cols > dir->max_cols)
  513.       dir->cols = dir->max_cols;
  514.  
  515.  
  516.    /*
  517.     * Find the maximun number of file names we can display in help screen.
  518.     */
  519.    dir->avail = dir->lines * dir->cols;
  520.  
  521.    /*
  522.     * Now find the number of file names we do have on the screen.  Every
  523.     *  time we slide the "window", we have to calculate a new nfiles.
  524.     */
  525.    dir->nfiles = dir->cnt > dir->avail ? dir->avail : dir->cnt;
  526.  
  527.    /*
  528.     * A lot of times, the number of matching files will not fit evenly
  529.     *  in our help screen.  The last column on the right will be partially
  530.     *  filled, hence the variable name prow (partial row).  When there are
  531.     *  more file names than can fit on the screen, we have to calculate
  532.     *  prow every time we slide the "window" of files.
  533.     */
  534.    dir->prow = dir->lines - (dir->avail - dir->nfiles);
  535.  
  536.    /*
  537.     * Find out how many "virtual" columns of file names we have.  If
  538.     *  all the files can fit in the dir screen, there will be no
  539.     *  virtual columns.
  540.     */
  541.    if (dir->cnt < dir->avail)
  542.       dir->vcols = 0;
  543.    else
  544.       dir->vcols =  (dir->cnt - dir->avail) / dir->max_lines +
  545.                    ((dir->cnt - dir->avail) % dir->max_lines ? 1 : 0);
  546.  
  547.    /*
  548.     * Find the physical display column in dir screen.
  549.     */
  550.    dir->flist_col[0] = dir->col + 2;
  551.    for (i=1; i<dir->max_cols; i++)
  552.       dir->flist_col[i] = dir->flist_col[i-1] + 17;
  553.  
  554.    /*
  555.     * Now, draw the borders of the dir screen.
  556.     */
  557.    for (i=0; i < dir->hgt; i++) {
  558.       if (i == 0 || i == dir->hgt-1) {
  559.          memset( temp, HORIZONTAL_LINE, wid );
  560.          temp[wid] = '\0';
  561.          if (i == 0) {
  562.             temp[0] = CORNER_LEFT_UP;
  563.             temp[wid-1] = CORNER_RIGHT_UP;
  564.          } else {
  565.             temp[0] = CORNER_LEFT_DOWN;
  566.             temp[wid-1] = CORNER_RIGHT_DOWN;
  567.          }
  568.       } else {
  569.          memset( temp, ' ', wid );
  570.          temp[wid] = '\0';
  571.          temp[0] = temp[wid-1] = VERTICAL_LINE;
  572.       }
  573.       s_output( temp, dir->row+i, dir->col, g_display.help_color );
  574.    }
  575.  
  576.    /*
  577.     * Write headings in help screen.
  578.     */
  579.    s_output( dir1, dir->row+DIR1_ROW, dir->col+DIR1_COL, g_display.help_color );
  580.    s_output( dir4, dir->row+DIR4_ROW, dir->col+DIR4_COL, g_display.help_color );
  581.    s_output( dir5, dir->row+DIR5_ROW, dir->col+DIR5_COL, g_display.help_color );
  582.    s_output( dir6, dir->row+DIR6_ROW, dir->col+DIR6_COL, g_display.help_color );
  583.    s_output( dir7, dir->row+DIR7_ROW, dir->col+DIR7_COL, g_display.help_color );
  584. }
  585.  
  586.  
  587. /*
  588.  * Name:    write_directory_list
  589.  * Purpose: given directory list, display matching files
  590.  * Date:    November 13, 1993
  591.  * Passed:  flist: pointer to list of files
  592.  *          dir:   directory display structure
  593.  * Notes:   blank out the previous file name and display the new one.
  594.  */
  595. void write_directory_list( FTYPE *flist, DIRECTORY dir )
  596. {
  597. FTYPE *p, *top;
  598. int  i;
  599. int  j;
  600. int  k;
  601. int  end;
  602. int  line;
  603. int  col;
  604. int  color;
  605. char temp[NAME_MAX+2];
  606. char blank[20];         /* blank out file names */
  607.  
  608.    memset( blank, ' ', 15 );
  609.    blank[15] = '\0';
  610.    color = g_display.help_color;
  611.    top = flist;
  612.    for (i=0; i < dir.lines; ++i) {
  613.       p = top;
  614.       end = FALSE;
  615.       for (j=0; j < dir.cols; ++j) {
  616.          col = dir.flist_col[j];
  617.          line = i + dir.row + 5;
  618.  
  619.          /*
  620.           * We need to blank out all lines and columns used to display
  621.           *  files, because there may be some residue from a previous dir
  622.           */
  623.          s_output( blank, line, col, color );
  624.          if (!end) {
  625.             if (strlen( p->fname ) <= 15)
  626.                s_output( p->fname, line, col, color );
  627.             else {
  628.  
  629.                assert( strlen( p->fname ) < NAME_MAX + 1 );
  630.  
  631.                strcpy( temp, p->fname );
  632.                temp[15] = '\0';
  633.                s_output( temp, line, col, color );
  634.             }
  635.             p += dir.lines;
  636.             k = p - flist;
  637.             if (k >= dir.nfiles)
  638.                end = TRUE;
  639.          }
  640.       }
  641.       ++top;
  642.    }
  643. }
  644.  
  645.  
  646. /*
  647.  * Name:     select_file
  648.  * Purpose:  To let user select a file from dir list
  649.  * Date:     November 13, 1993
  650.  * Modified: November 13, 1993, Frank Davis per Byrial Jensen
  651.  * Passed:   flist: pointer to list of files
  652.  *           stem:  base directory
  653.  *           dir:   directory display stuff
  654.  * Notes:    let user move thru the file names with the cursor keys
  655.  */
  656. int  select_file( FTYPE *flist, char *stem, DIRECTORY *dir )
  657. {
  658. int  ch;                /* input character from user */
  659. int  func;              /* function of character input by user */
  660. int  fno;               /* index into flist of the file under cursor */
  661. int  goodkey;           /* is key a recognized function key? */
  662. int  r;                 /* current row of cursor */
  663. int  c;                 /* current column of cursor */
  664. int  offset;            /* offset into file list */
  665. int  stop;              /* stop indicator */
  666. int  color;             /* color of help screen */
  667. int  file_color;        /* color of current file */
  668. int  change;            /* boolean, hilite another file? */
  669. int  oldr;              /* old row */
  670. int  oldc;              /* old column */
  671. char asize[20];         /* ascii file size */
  672. char blank[NAME_MAX];   /* blank out file names */
  673. char temp[PATH_MAX];
  674.  
  675.    /*
  676.     * initial everything.
  677.     */
  678.    memset( blank, ' ', 15 );
  679.    blank[15] = '\0';
  680.    c = r = 1;
  681.    ch = fno = offset = 0;
  682.    color = g_display.help_color;
  683.    file_color = g_display.hilited_file;
  684.    goodkey = TRUE;
  685.    stop = FALSE;
  686.  
  687.    /*
  688.     * UNIX directory names can get quite long.  lets only show the first 50.
  689.     */
  690.    if (strlen( stem ) < 50)
  691.       s_output( stem, dir->row+DIR1_ROW, dir->col+strlen( dir1 )+3, color );
  692.    else {
  693.       strcpy( temp, stem );
  694.       temp[50] = '\0';
  695.       s_output( temp, dir->row+DIR1_ROW, dir->col+strlen( dir1 )+3, color );
  696.    }
  697.  
  698.    if (strlen( flist[fno].fname ) < 50 )
  699.       s_output( flist[fno].fname, dir->row+DIR4_ROW,
  700.                 dir->col+strlen( dir4 )+3, color );
  701.    else {
  702.       strcpy( temp, flist[fno].fname );
  703.       temp[50] = '\0';
  704.       s_output( temp, dir->row+DIR4_ROW, dir->col+strlen( dir4 )+3, color );
  705.    }
  706.    my_ltoa( flist[fno].fsize, asize, 10 );
  707.    s_output( blank, dir->row+DIR5_ROW, dir->col+strlen( dir5 )+3, color );
  708.    s_output( asize, dir->row+DIR5_ROW, dir->col+strlen( dir5 )+3, color );
  709.    my_ltoa( dir->cnt,  asize, 10 );
  710.    s_output( blank, dir->row+DIR6_ROW, dir->col+DIR6_COL+strlen( dir6 ), color);
  711.    s_output( asize, dir->row+DIR6_ROW, dir->col+DIR6_COL+strlen( dir6 ), color);
  712.    xygoto( (c-1)*17+dir->col+2, r+dir->row+4 );
  713.    hlight_line( (c-1)*17+dir->col+2, r+dir->row+4, 15, file_color );
  714.    change = FALSE;
  715.    func = AbortCommand;
  716.    while (stop == FALSE) {
  717.       oldr = r;
  718.       oldc = c;
  719.       ch = getkey( );
  720.       func = getfunc( ch );
  721.  
  722.       /*
  723.        * User may have redefined the Enter and ESC keys.  Make the Enter key
  724.        *  perform a Rturn in this function. Make the ESC key do an AbortCommand.
  725.        */
  726.       if (ch == RTURN)
  727.          func = Rturn;
  728.       else if (ch == ESC)
  729.          func = AbortCommand;
  730.  
  731.       switch (func) {
  732.          case Rturn       :
  733.          case NextLine    :
  734.          case BegNextLine :
  735.             stop = TRUE;
  736.             break;
  737.          case AbortCommand :
  738.             stop = TRUE;
  739.             break;
  740.          case LineUp :
  741.             if (r > 1) {
  742.                change = TRUE;
  743.                --r;
  744.             } else {
  745.                r = dir->lines;
  746.                change = TRUE;
  747.                if (offset == 0 || c > 1) {
  748.                   if (c > 1)
  749.                      --c;
  750.                } else if (dir->vcols > 0 && offset > 0 && c == 1) {
  751.                   /*
  752.                    * recalculate the dir display stuff.
  753.                    */
  754.                   offset -= dir->lines;
  755.                   recalculate_dir( dir, flist, offset );
  756.                }
  757.             }
  758.             goodkey = TRUE;
  759.             break;
  760.          case LineDown :
  761.             if (r < dir->prow) {
  762.                change = TRUE;
  763.                ++r;
  764.             } else if (r < dir->lines && c != dir->cols) {
  765.                change = TRUE;
  766.                ++r;
  767.             } else {
  768.                change = TRUE;
  769.                r = 1;
  770.                if (offset == dir->vcols * dir->lines || c < dir->cols) {
  771.                   if (c < dir->cols)
  772.                      ++c;
  773.                } else if (dir->vcols > 0 && offset < dir->vcols * dir->lines &&
  774.                          c == dir->cols) {
  775.                   offset += dir->lines;
  776.                   recalculate_dir( dir, flist, offset );
  777.                }
  778.             }
  779.             goodkey = TRUE;
  780.             break;
  781.          case CharLeft :
  782.             if (offset == 0 || c > 1) {
  783.                if (c > 1) {
  784.                   change = TRUE;
  785.                   --c;
  786.                }
  787.             } else if (dir->vcols > 0 && offset > 0 && c == 1) {
  788.                change = TRUE;
  789.  
  790.                /*
  791.                 * recalculate the dir display stuff.
  792.                 */
  793.                offset -= dir->lines;
  794.                recalculate_dir( dir, flist, offset );
  795.             }
  796.             goodkey = TRUE;
  797.             break;
  798.          case CharRight :
  799.             if (offset == dir->vcols * dir->lines || c < dir->cols) {
  800.                if (c < dir->cols) {
  801.                   change = TRUE;
  802.                   ++c;
  803.                   if (c == dir->cols) {
  804.                      if ( r > dir->prow)
  805.                         r = dir->prow;
  806.                   }
  807.                }
  808.             } else if (dir->vcols > 0 && offset < dir->vcols * dir->lines &&
  809.                          c == dir->cols) {
  810.                change = TRUE;
  811.                offset += dir->lines;
  812.                recalculate_dir( dir, flist, offset );
  813.                if (r > dir->prow)
  814.                   r = dir->prow;
  815.             }
  816.             goodkey = TRUE;
  817.             break;
  818.          case BegOfLine :
  819.             change = TRUE;
  820.             c = r = 1;
  821.             goodkey = TRUE;
  822.             break;
  823.          case EndOfLine :
  824.             change = TRUE;
  825.             r = dir->prow;
  826.             c = dir->cols;
  827.             goodkey = TRUE;
  828.             break;
  829.          case ScreenDown :
  830.             change = TRUE;
  831.             r = (c == dir->cols) ? r = dir->prow : dir->lines;
  832.             goodkey = TRUE;
  833.             break;
  834.          case ScreenUp :
  835.             change = TRUE;
  836.             r = 1;
  837.             goodkey = TRUE;
  838.             break;
  839.          default :
  840.             break;
  841.       }
  842.       if (goodkey) {
  843.          memset( temp, ' ', 50 );
  844.          temp[50] = '\0';
  845.          s_output( temp, dir->row+DIR4_ROW, dir->col+strlen( dir4 )+3, color );
  846.          fno = offset + (c-1)*dir->lines + (r-1);
  847.          if (strlen( flist[fno].fname ) < 50)
  848.             s_output( flist[fno].fname, dir->row+DIR4_ROW,
  849.                          dir->col+strlen( dir4 )+3, color );
  850.          else {
  851.             strcpy( temp, flist[fno].fname );
  852.             temp[50] = '\0';
  853.             s_output( temp, dir->row+DIR4_ROW, dir->col+strlen( dir4 )+3,color);
  854.          }
  855.          my_ltoa( flist[fno].fsize, asize, 10 );
  856.          s_output( blank, dir->row+DIR5_ROW, dir->col+strlen( dir5 )+3, color );
  857.          s_output( asize, dir->row+DIR5_ROW, dir->col+strlen( dir5 )+3, color );
  858.  
  859.          xygoto( (c-1)*17+dir->col+2, r+dir->row+4 );
  860.          goodkey = FALSE;
  861.          if (change) {
  862.             hlight_line( (oldc-1)*17+dir->col+2, oldr+dir->row+4, 15, color );
  863.             hlight_line( (c-1)*17+dir->col+2, r+dir->row+4, 15, file_color );
  864.             change = FALSE;
  865.          }
  866.       }
  867.    }
  868.    dir->select = fno;
  869.    return( func == AbortCommand ? ERROR : OK );
  870. }
  871.  
  872. #else
  873. /*
  874.  **********************************************************************
  875.  ******************************  PART 2  ******************************
  876.  **********************************************************************
  877.  *
  878.  * Calls to BIOS and writes to PC hardware.
  879.  */
  880.  
  881. /*
  882.  * Name:    dir_help
  883.  * Purpose: To prompt the user and list the directory contents
  884.  * Date:    February 13, 1992
  885.  * Passed:  window:  pointer to current window
  886.  */
  887. int  dir_help( TDE_WIN *window )
  888. {
  889. char dname[MAX_COLS+2]; /* directory search pattern */
  890. char stem[MAX_COLS+2];  /* directory stem */
  891. char drive[_MAX_DRIVE]; /* splitpath drive buff */
  892. char dir[_MAX_DIR];     /* splitpath dir buff */
  893. char fname[_MAX_FNAME]; /* splitpath fname buff */
  894. char ext[_MAX_EXT];     /* splitpath ext buff */
  895. int  rc;
  896. int  file_mode;
  897. int  bin_length;
  898. int  prompt_line;
  899.  
  900.    if (window != NULL) {
  901.       entab_linebuff( );
  902.       if (un_copy_line( window->ll, window, TRUE ) == ERROR)
  903.          return( ERROR );
  904.       prompt_line = window->bottom_line;
  905.    } else
  906.       prompt_line = g_display.nlines;
  907.  
  908.    /*
  909.     * search path or pattern
  910.     */
  911.    dname[0] = '\0';
  912.    rc = get_name( dir1, prompt_line, dname, g_display.message_color );
  913.  
  914.    if (rc == OK) {
  915.       if (validate_path( dname, stem ) == OK) {
  916.          rc = list_and_pick( dname, stem, window );
  917.  
  918.          /*
  919.           * if everything is everything, load in the file selected by user.
  920.           */
  921.          if (rc == OK) {
  922.             file_mode = TEXT;
  923.             bin_length = 0;
  924.             _splitpath( dname, drive, dir, fname, ext );
  925.             if (stricmp( ext, ".exe" ) == 0  ||  stricmp( ext, ".com" ) == 0) {
  926.                file_mode = BINARY;
  927.                bin_length = g_status.file_chunk;
  928.             }
  929.             if (window != NULL)
  930.                attempt_edit_display( dname, LOCAL, file_mode, bin_length );
  931.             else
  932.                attempt_edit_display( dname, GLOBAL, file_mode, bin_length );
  933.          }
  934.       } else
  935.          /*
  936.           * invalid path or file name
  937.           */
  938.          error( WARNING,
  939.                 window != NULL ? window->bottom_line : g_display.nlines, dir2 );
  940.    }
  941.    return( rc );
  942. }
  943.  
  944.  
  945. /*
  946.  * Name:    validate_path
  947.  * Purpose: make sure we got a valid search pattern or subdirectory
  948.  * Date:    February 13, 1992
  949.  * Passed:  dname: search path entered by user
  950.  *          stem:  directory stem is returned
  951.  * Returns: successful or not
  952.  * Notes:   we need to validate the search path or pattern.  if the search
  953.  *            pattern is valid, then we need to get the search stem.
  954.  *          the user may enter a subdirectory or some kind of search pattern.
  955.  *             if the user enters a subdirectory, then there are a few things
  956.  *             we need to take care of  1) find out if the subdirectory is
  957.  *             the root, 2) append a '\' to the subdirectory so we can create
  958.  *             a search pattern for the subdirectory, 3) don't append '\' to
  959.  *             the root, it already has a '\'.
  960.  *          if the user enters a search pattern, then we need to dissect the
  961.  *             search path.  we must create a stem from the search pattern.
  962.  */
  963. int  validate_path( char *dname, char *stem )
  964. {
  965. int  rc;
  966. DTA  dta;               /* temp disk transfer struct for findfirst, etc. */
  967. int  fattr;
  968. int  i;
  969. int  len;
  970. char *p;
  971. char temp[MAX_COLS+2];  /* directory stem */
  972.  
  973.    /*
  974.     * if path name is void then the current working directory is implied.
  975.     */
  976.    if (dname[0] == '\0') {
  977.  
  978.       assert( strlen( stardotstar ) < MAX_COLS );
  979.  
  980.       strcpy( dname, stardotstar );
  981.       stem[0] = '\0';
  982.       rc = OK;
  983.    } else {
  984.  
  985.       /*
  986.        * get the attributes of the search pattern, so we can determine if
  987.        * this is a pattern or subdirectory.
  988.        */
  989.       rc = get_fattr( dname, &fattr );
  990.  
  991.       if (rc == OK && (fattr & SUBDIRECTORY)) {
  992.          assert( strlen( dname ) < MAX_COLS );
  993.          strcpy( stem, dname );
  994.  
  995.          /*
  996.           * if this is the root directory ( \ ), don't append '\' to it.
  997.           * user entered a subdirectory - append *.* to get contents of
  998.           * subdirectory.
  999.           */
  1000.          len = strlen( stem );
  1001.          if (stem[len-1] != '\\') {
  1002.             strcat( stem, "\\" );
  1003.             strcat( dname, "\\" );
  1004.          }
  1005.          strcat( dname, stardotstar );
  1006.  
  1007.       /*
  1008.        * not a subdirectory.  let's see if any files match the search
  1009.        * pattern.
  1010.        */
  1011.       } else if (rc != ERROR) {
  1012.          if ((rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN |
  1013.                               SYSTEM | SUBDIRECTORY | ARCHIVE )) == OK) {
  1014.  
  1015.             /*
  1016.              * copy dname to "temp" so we can use "temp" to find the stem.
  1017.              *    we need to search the pattern backwards to figure the stem.
  1018.              */
  1019.  
  1020.             assert( strlen( dname ) < MAX_COLS );
  1021.  
  1022.             strcpy( temp, dname );
  1023.             len = strlen( dname );
  1024.             for (i=len,p=temp+len; i>=0; i--) {
  1025.                /*
  1026.                 *  if we run into the '\' or the ':', then we got a stem.
  1027.                 */
  1028.                if (*p == '\\' || *p == ':') {
  1029.                   p = temp + i;
  1030.                   *(p+1) = '\0';
  1031.                   break;
  1032.                /*
  1033.                 * if we're at the beginning of the string, stem == '\0'
  1034.                 */
  1035.                } else if (i == 0) {
  1036.                   *p = '\0';
  1037.                   break;
  1038.                }
  1039.                --p;
  1040.             }
  1041.  
  1042.             assert( strlen( temp ) < MAX_COLS );
  1043.  
  1044.             strcpy( stem, temp );
  1045.          } else
  1046.             rc = ERROR;
  1047.  
  1048.       /*
  1049.        * user did not enter a valid subdirectory name or search pattern.
  1050.        */
  1051.       } else
  1052.          rc = ERROR;
  1053.    }
  1054.    return( rc );
  1055. }
  1056.  
  1057.  
  1058. /*
  1059.  * Name:    list_and_pick
  1060.  * Purpose: To show matching file names and let user pick a file
  1061.  * Date:    February 13, 1992
  1062.  * Passed:  dname:  directory search pattern
  1063.  *          stem:   stem of directory search pattern
  1064.  *          window:  pointer to current window
  1065.  * Returns: return code from pick.  rc = OK, then edit a new file.
  1066.  * Notes:   real work routine of this function.  save the cwd and let the
  1067.  *           user search upwards or downwards thru the directory structure.
  1068.  *          since we are doing DOS directory functions, we need to check the
  1069.  *           return code after each DOS call for critical errors.
  1070.  */
  1071. int  list_and_pick( char *dname, char *stem, TDE_WIN *window )
  1072. {
  1073. int  rc;
  1074. DTA  dta;               /* disk transfer address for findfirst */
  1075. DIRECTORY dir;          /* contains all info for dir display */
  1076. unsigned int cnt;       /* number of matching files */
  1077. FTYPE *flist, *p;       /* pointer to list of matching files */
  1078. char cwd[MAX_COLS];     /* save the current working directory in this buff */
  1079. char dbuff[MAX_COLS];   /* temporary directory buff */
  1080. char prefix[MAX_COLS];  /* directory prefix  */
  1081. int  change_directory = FALSE;
  1082. int  stop;
  1083. int  len;
  1084. int  drive;
  1085.  
  1086.    /*
  1087.     * Some algorithms alloc the maximum possible number of files in
  1088.     *  a directory, eg. 256 or 512.  Let's count the number of matching
  1089.     *  files so we know egxactly how much memory to request from calloc.
  1090.     *  Depending on the op system, disk media, disk format, or version of DOS,
  1091.     *  the max number of files may vary, anyway, also, additionally.
  1092.     *  (see the unix section above, where the number of files changes with
  1093.     *   each port of unix to new toasters.)
  1094.     */
  1095.    rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN | SYSTEM |
  1096.                                 SUBDIRECTORY | ARCHIVE );
  1097.    if (rc != ERROR) {
  1098.       for (cnt=1; (rc = my_findnext( &dta )) == OK;)
  1099.          ++cnt;
  1100.       flist = (FTYPE *)calloc( cnt, sizeof(FTYPE) );
  1101.    } else
  1102.       flist = NULL;
  1103.    if (rc != ERROR && flist != NULL) {
  1104.  
  1105.       stop = FALSE;
  1106.       /*
  1107.        * If user entered drive name in search pattern, find out the drive and
  1108.        *  directory stem.
  1109.        */
  1110.       if (stem[1] == ':') {
  1111.  
  1112.          /*
  1113.           * If the second character of the search pattern is a ':', the
  1114.           *  the first character of the pattern should be the drive.
  1115.           *  Convert drive to lower case and get a numerical representation.
  1116.           * CAVEAT:  In DOS v 2.x, there may be up to 63 logical drives.
  1117.           *   my algorithm may blow up if the number of logical drives
  1118.           *   is greater than 'Z'.
  1119.           * For DOS >= 3, the number of drives is limited to 26, I think.
  1120.           */
  1121.          drive = stem[0];
  1122.          if (drive < 'a')
  1123.             drive += 32;
  1124.          drive = drive - 'a' + 1;
  1125.          rc = get_current_directory( dbuff, drive );
  1126.          if (rc == ERROR)
  1127.             stop = TRUE;
  1128.          else {
  1129.  
  1130.             /*
  1131.              * Put drive letter, ':', and '\' in front of current directory.
  1132.              */
  1133.             prefix[0] = (char)(drive - 1 + 'a');
  1134.             prefix[1] = ':';
  1135.             prefix[2] = '\\';
  1136.             prefix[3] = '\0';
  1137.             assert( strlen( prefix ) + strlen( dbuff ) < MAX_COLS );
  1138.             strcpy( cwd, prefix );
  1139.             strcat( cwd, dbuff );
  1140.          }
  1141.  
  1142.       /*
  1143.        * else get current directory from default drive
  1144.        */
  1145.       } else {
  1146.  
  1147.          /*
  1148.           * 0 = default drive.
  1149.           */
  1150.          drive = 0;
  1151.          rc = get_current_directory( dbuff, drive );
  1152.          if (rc == ERROR)
  1153.             stop = TRUE;
  1154.          else {
  1155.  
  1156.             /*
  1157.              * Put a '\' in front of the current directory.
  1158.              */
  1159.             prefix[0] = '\\';
  1160.             prefix[1] = '\0';
  1161.  
  1162.             assert( strlen( prefix ) + strlen( dbuff ) < MAX_COLS );
  1163.  
  1164.             strcpy( cwd, prefix );
  1165.             strcat( cwd, dbuff );
  1166.          }
  1167.       }
  1168.  
  1169.       while (stop == FALSE) {
  1170.          /*
  1171.           * If we had enough memory, find all matching file names.  Append
  1172.           *  '\\' at the end of subdirectory names so user will know if
  1173.           *  name is a directory.  Might as well find everything, because
  1174.           *  i also forget subdirectory names, too.
  1175.           *
  1176.           * when we get here, we have already done: 1) my_findfirst and
  1177.           *  my_findnext, 2) counted the number of matching files, and
  1178.           *  3) allocated space.
  1179.           */
  1180.          p = flist;
  1181.          cnt = 0;
  1182.  
  1183.          rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN | SYSTEM |
  1184.                                  SUBDIRECTORY | ARCHIVE );
  1185.          if (rc != ERROR) {
  1186.  
  1187.             /*
  1188.              * p is pointer that walks down the file info structure.
  1189.              *  save the file name, file size, and directory character,
  1190.              *  if needed, for each matching file we find.
  1191.              */
  1192.  
  1193.             assert( strlen( dta.name ) < 14 );
  1194.  
  1195.             strcpy( p->fname, dta.name );
  1196.             p->fsize = dta.size;
  1197.             if (dta.attrib & SUBDIRECTORY)
  1198.                strcat( p->fname, "\\" );
  1199.             for (cnt=1; (rc = my_findnext( &dta )) == OK; ) {
  1200.                ++p;
  1201.  
  1202.                assert( strlen( dta.name ) < 14 );
  1203.  
  1204.                strcpy( p->fname, dta.name );
  1205.                p->fsize = dta.size;
  1206.                if (dta.attrib & SUBDIRECTORY)
  1207.                   strcat( p->fname, "\\" );
  1208.                cnt++;
  1209.             }
  1210.          }
  1211.  
  1212.          if (rc != ERROR) {
  1213.             shell_sort( flist, cnt );
  1214.  
  1215.             /*
  1216.              * figure out number of rows, cols, etc... then display dir list
  1217.              */
  1218.             setup_directory_window( &dir, cnt );
  1219.             write_directory_list( flist, dir );
  1220.  
  1221.             /*
  1222.              * Let user select file name or another search directory.
  1223.              *  Save the choice in dbuff.  rc == OK if user selected file or dir.
  1224.              */
  1225.             rc = select_file( flist, stem, &dir );
  1226.  
  1227.             assert( strlen( flist[dir.select].fname ) < MAX_COLS );
  1228.  
  1229.             strcpy( dbuff, flist[dir.select].fname );
  1230.          }
  1231.  
  1232.          /*
  1233.           *  give memory back.
  1234.           */
  1235.          free( flist );
  1236.          flist = NULL;
  1237.  
  1238.          if (rc == ERROR)
  1239.             stop = TRUE;
  1240.          else {
  1241.             len = strlen( dbuff );
  1242.  
  1243.             /*
  1244.              * If the last character in a file name is '\' then let's
  1245.              *  do a dir on selected directory.  See the matching
  1246.              *  else when the user selects a file.
  1247.              */
  1248.             if (dbuff[len-1] == '\\') {
  1249.  
  1250.                /*
  1251.                 * Stem has subdirectory path.  dbuff has selected path.
  1252.                 * Create a new dname with stem and dbuff.
  1253.                 */
  1254.  
  1255.                assert( strlen( stem ) + strlen( dbuff ) < MAX_COLS );
  1256.  
  1257.                strcpy( dname, stem );
  1258.                strcat( dname, dbuff );
  1259.                len = strlen( dname );
  1260.                strcpy( dbuff, dname );
  1261.  
  1262.                /*
  1263.                 * The last character in dbuff is '\', because we append the
  1264.                 *  '\' to every directory entry in the file list.  Replace
  1265.                 *  it with a NULL char then we will have a valid path name.
  1266.                 */
  1267.                dbuff[len-1] = '\0';
  1268.  
  1269.                /*
  1270.                 * now let's change to the selected subdirectory.
  1271.                 */
  1272.                rc = set_current_directory( dbuff );
  1273.                if (rc == OK) {
  1274.  
  1275.                   /*
  1276.                    * Every time we change directories, we need to get the
  1277.                    *  current directory so we will be sure to have the
  1278.                    *  correct path.
  1279.                    */
  1280.                   rc = get_current_directory( dbuff, drive );
  1281.                   if (rc == OK) {
  1282.  
  1283.                      assert( strlen( prefix ) + strlen( dbuff ) < MAX_COLS );
  1284.  
  1285.                      strcpy( dname, prefix );
  1286.                      strcat( dname, dbuff );
  1287.                      change_directory = TRUE;
  1288.                   }
  1289.                }
  1290.  
  1291.                /*
  1292.                 * Validate the new path and allocate memory for the
  1293.                 *  matching files.
  1294.                 */
  1295.                if (rc == OK)
  1296.                   rc = validate_path( dname, stem );
  1297.                if (rc == OK) {
  1298.                   rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN |
  1299.                                   SYSTEM | SUBDIRECTORY | ARCHIVE );
  1300.                   if (rc != ERROR) {
  1301.                      for (cnt=1; (rc = my_findnext( &dta )) == OK;)
  1302.                         ++cnt;
  1303.                      flist = (FTYPE *)calloc( cnt, sizeof(FTYPE) );
  1304.                   }
  1305.                }
  1306.                if (flist == NULL || rc == ERROR) {
  1307.                   stop = TRUE;
  1308.                   rc = ERROR;
  1309.                   if (flist != NULL) {
  1310.                      free( flist );
  1311.                      flist = NULL;
  1312.                   }
  1313.                }
  1314.             } else {
  1315.  
  1316.                /*
  1317.                 * user selected a file.  store fname in dname and return.
  1318.                 */
  1319.                rc = OK;
  1320.                stop = TRUE;
  1321.  
  1322.                assert( strlen( stem ) + strlen( dbuff ) < MAX_COLS );
  1323.  
  1324.                strcpy( dname, stem );
  1325.                strcat( dname, dbuff );
  1326.             }
  1327.          }
  1328.       }
  1329.  
  1330.       /*
  1331.        * Go back to the current directory if needed.
  1332.        */
  1333.       if (change_directory)
  1334.          set_current_directory( cwd );
  1335.       if (window != NULL)
  1336.          redraw_screen( window );
  1337.    } else {
  1338.       /*
  1339.        * out of memory or bad dir
  1340.        */
  1341.       error( WARNING,  window != NULL ? window->bottom_line : g_display.nlines,
  1342.              dir3 );
  1343.       rc = ERROR;
  1344.    }
  1345.    return( rc );
  1346. }
  1347.  
  1348.  
  1349. /*
  1350.  * Name:    setup_directory_window
  1351.  * Purpose: set number of rows and cols in directory window
  1352.  * Date:    February 13, 1992
  1353.  * Passed:  dir: pointer to directory structure
  1354.  *          cnt: number of files
  1355.  * Notes:   set up stuff we need to know about how to display files.
  1356.  */
  1357. void setup_directory_window( DIRECTORY *dir, int cnt )
  1358. {
  1359. int  i;
  1360. int  wid;
  1361. char temp[MAX_COLS];    /* line to output */
  1362.  
  1363.    /*
  1364.     * setup the fixed vars used in dir display.
  1365.     *    dir->col =      physical upper left column of dir screen
  1366.     *    dir->row =      physical upper left row or line of dir screen
  1367.     *    dir->wid =      width of physical screen
  1368.     *    dir->hgt =      height of physical screen
  1369.     *    dir->max_cols   number of columns of files in dir screen
  1370.     *    dir->max_lines  number of lines of files in each column in dir screen
  1371.     *    dir->cnt        number of files in list
  1372.     */
  1373.    dir->col = 3;
  1374.    dir->row = 5;
  1375.    wid = dir->wid = 72;
  1376.    dir->hgt = 16;
  1377.    dir->max_cols = 5;
  1378.    dir->max_lines = 9;
  1379.    dir->cnt = cnt;
  1380.  
  1381.    /*
  1382.     * Find out how many lines in each column are needed to display
  1383.     *  matching files.
  1384.     */
  1385.    dir->lines = dir->cnt / dir->max_cols + (dir->cnt % dir->max_cols ? 1 : 0);
  1386.    if (dir->lines > dir->max_lines)
  1387.       dir->lines = dir->max_lines;
  1388.  
  1389.    /*
  1390.     * Find out how many columns of file names we need.
  1391.     */
  1392.    dir->cols = dir->cnt / dir->lines + (dir->cnt % dir->lines ? 1 : 0);
  1393.    if (dir->cols > dir->max_cols)
  1394.       dir->cols = dir->max_cols;
  1395.  
  1396.  
  1397.    /*
  1398.     * Find the maximun number of file names we can display in help screen.
  1399.     */
  1400.    dir->avail = dir->lines * dir->cols;
  1401.  
  1402.    /*
  1403.     * Now find the number of file names we do have on the screen.  Every
  1404.     *  time we slide the "window", we have to calculate a new nfiles.
  1405.     */
  1406.    dir->nfiles = dir->cnt > dir->avail ? dir->avail : dir->cnt;
  1407.  
  1408.    /*
  1409.     * A lot of times, the number of matching files will not fit evenly
  1410.     *  in our help screen.  The last column on the right will be partially
  1411.     *  filled, hence the variable name prow (partial row).  When there are
  1412.     *  more file names than can fit on the screen, we have to calculate
  1413.     *  prow every time we slide the "window" of files.
  1414.     */
  1415.    dir->prow = dir->lines - (dir->avail - dir->nfiles);
  1416.  
  1417.    /*
  1418.     * Find out how many "virtual" columns of file names we have.  If
  1419.     *  all the files can fit in the dir screen, there will be no
  1420.     *  virtual columns.
  1421.     */
  1422.    if (dir->cnt < dir->avail)
  1423.       dir->vcols = 0;
  1424.    else
  1425.       dir->vcols =  (dir->cnt - dir->avail) / dir->max_lines +
  1426.                    ((dir->cnt - dir->avail) % dir->max_lines ? 1 : 0);
  1427.  
  1428.    /*
  1429.     * Find the physical display column in dir screen.
  1430.     */
  1431.    dir->flist_col[0] = dir->col + 2;
  1432.    for (i=1; i<dir->max_cols; i++)
  1433.       dir->flist_col[i] = dir->flist_col[i-1] + 14;
  1434.  
  1435.    /*
  1436.     * Now, draw the borders of the dir screen.
  1437.     */
  1438.    for (i=0; i < dir->hgt; i++) {
  1439.       if (i == 0 || i == dir->hgt-1) {
  1440.          memset( temp, HORIZONTAL_LINE, wid );
  1441.          temp[wid] = '\0';
  1442.          if (i == 0) {
  1443.             temp[0] = CORNER_LEFT_UP;
  1444.             temp[wid-1] = CORNER_RIGHT_UP;
  1445.          } else {
  1446.             temp[0] = CORNER_LEFT_DOWN;
  1447.             temp[wid-1] = CORNER_RIGHT_DOWN;
  1448.          }
  1449.       } else {
  1450.          memset( temp, ' ', wid );
  1451.          temp[wid] = '\0';
  1452.          temp[0] = temp[wid-1] = VERTICAL_LINE;
  1453.       }
  1454.       s_output( temp, dir->row+i, dir->col, g_display.help_color );
  1455.    }
  1456.  
  1457.    /*
  1458.     * Write headings in help screen.
  1459.     */
  1460.    s_output( dir4, dir->row+DIR4_ROW, dir->col+DIR4_COL, g_display.help_color );
  1461.    s_output( dir5, dir->row+DIR5_ROW, dir->col+DIR5_COL, g_display.help_color );
  1462.    s_output( dir6, dir->row+DIR6_ROW, dir->col+DIR6_COL, g_display.help_color );
  1463.    s_output( dir7, dir->row+DIR7_ROW, dir->col+DIR7_COL, g_display.help_color );
  1464. }
  1465.  
  1466.  
  1467. /*
  1468.  * Name:    write_directory_list
  1469.  * Purpose: given directory list, display matching files
  1470.  * Date:    February 13, 1992
  1471.  * Passed:  flist: pointer to list of files
  1472.  *          dir:   directory display structure
  1473.  * Notes:   blank out the previous file name and display the new one.
  1474.  */
  1475. void write_directory_list( FTYPE *flist, DIRECTORY dir )
  1476. {
  1477. FTYPE *p, *top;
  1478. int  i;
  1479. int  j;
  1480. int  k;
  1481. int  end;
  1482. int  line;
  1483. int  col;
  1484. int  color;
  1485. char blank[20];         /* blank out file names */
  1486.  
  1487.    memset( blank, ' ', 12 );
  1488.    blank[12] = '\0';
  1489.    color = g_display.help_color;
  1490.    top = flist;
  1491.    for (i=0; i < dir.lines; ++i) {
  1492.       p = top;
  1493.       end = FALSE;
  1494.       for (j=0; j < dir.cols; ++j) {
  1495.          col = dir.flist_col[j];
  1496.          line = i + dir.row + 4;
  1497.  
  1498.          /*
  1499.           * We need to blank out all lines and columns used to display
  1500.           *  files, because there may be some residue from a previous dir
  1501.           */
  1502.          s_output( blank, line, col, color );
  1503.          if (!end) {
  1504.             s_output( p->fname, line, col, color );
  1505.             p += dir.lines;
  1506.             k = p - flist;
  1507.             if (k >= dir.nfiles)
  1508.                end = TRUE;
  1509.          }
  1510.       }
  1511.       ++top;
  1512.    }
  1513. }
  1514.  
  1515.  
  1516. /*
  1517.  * Name:    select_file
  1518.  * Purpose: To let user select a file from dir list
  1519.  * Date:    February 13, 1992
  1520.  * Passed:  flist: pointer to list of files
  1521.  *          stem:  base directory
  1522.  *          dir:   directory display stuff
  1523.  * Notes:   let user move thru the file names with the cursor keys
  1524.  */
  1525. int  select_file( FTYPE *flist, char *stem, DIRECTORY *dir )
  1526. {
  1527. int  ch;                /* input character from user */
  1528. int  func;              /* function of character input by user */
  1529. int  fno;               /* index into flist of the file under cursor */
  1530. int  goodkey;           /* is key a recognized function key? */
  1531. int  r;                 /* current row of cursor */
  1532. int  c;                 /* current column of cursor */
  1533. int  offset;            /* offset into file list */
  1534. int  stop;              /* stop indicator */
  1535. int  stem_len;          /* stem length */
  1536. int  color;             /* color of help screen */
  1537. int  file_color;        /* color of current file */
  1538. int  change;            /* boolean, hilite another file? */
  1539. int  oldr;              /* old row */
  1540. int  oldc;              /* old column */
  1541. char asize[20];         /* ascii file size */
  1542. char blank[20];         /* blank out file names */
  1543.  
  1544.    /*
  1545.     * initial everything.
  1546.     */
  1547.    memset( blank, ' ', 12 );
  1548.    blank[12] = '\0';
  1549.    c = r = 1;
  1550.    ch = fno = offset = 0;
  1551.    color = g_display.help_color;
  1552.    file_color = g_display.hilited_file;
  1553.    goodkey = TRUE;
  1554.    stop = FALSE;
  1555.    stem_len = strlen( stem );
  1556.    s_output( stem, dir->row+DIR4_ROW, dir->col+strlen( dir4 )+3, color );
  1557.    s_output( flist[fno].fname, dir->row + DIR4_ROW,
  1558.                 dir->col+strlen( dir4 )+3+stem_len, color );
  1559.    my_ltoa( flist[fno].fsize, asize, 10 );
  1560.    s_output( blank, dir->row+DIR5_ROW, dir->col+strlen( dir5 )+3, color );
  1561.    s_output( asize, dir->row+DIR5_ROW, dir->col+strlen( dir5 )+3, color );
  1562.    my_ltoa( dir->cnt,  asize, 10 );
  1563.    s_output( blank, dir->row+DIR6_ROW, dir->col+DIR6_COL+strlen( dir6 ), color);
  1564.    s_output( asize, dir->row+DIR6_ROW, dir->col+DIR6_COL+strlen( dir6 ), color);
  1565.    xygoto( (c-1)*14+dir->col+2, r+dir->row+3 );
  1566.    hlight_line( (c-1)*14+dir->col+2, r+dir->row+3, 12, file_color );
  1567.    change = FALSE;
  1568.    while (stop == FALSE) {
  1569.       oldr = r;
  1570.       oldc = c;
  1571.       ch = getkey( );
  1572.       func = getfunc( ch );
  1573.  
  1574.       /*
  1575.        * User may have redefined the Enter and ESC keys.  Make the Enter key
  1576.        *  perform a Rturn in this function. Make the ESC key do an AbortCommand.
  1577.        */
  1578.       if (ch == RTURN)
  1579.          func = Rturn;
  1580.       else if (ch == ESC)
  1581.          func = AbortCommand;
  1582.  
  1583.       switch (func) {
  1584.          case Rturn       :
  1585.          case NextLine    :
  1586.          case BegNextLine :
  1587.             stop = TRUE;
  1588.             break;
  1589.          case AbortCommand :
  1590.             stop = TRUE;
  1591.             break;
  1592.          case LineUp :
  1593.             if (r > 1) {
  1594.                change = TRUE;
  1595.                --r;
  1596.             } else {
  1597.                r = dir->lines;
  1598.                change = TRUE;
  1599.                if (offset == 0 || c > 1) {
  1600.                   if (c > 1)
  1601.                      --c;
  1602.                } else if (dir->vcols > 0 && offset > 0 && c == 1) {
  1603.                   /*
  1604.                    * recalculate the dir display stuff.
  1605.                    */
  1606.                   offset -= dir->lines;
  1607.                   recalculate_dir( dir, flist, offset );
  1608.                }
  1609.             }
  1610.             goodkey = TRUE;
  1611.             break;
  1612.          case LineDown :
  1613.             if (r < dir->prow) {
  1614.                change = TRUE;
  1615.                ++r;
  1616.             } else if (r < dir->lines && c != dir->cols) {
  1617.                change = TRUE;
  1618.                ++r;
  1619.             } else {
  1620.                change = TRUE;
  1621.                r = 1;
  1622.                if (offset == dir->vcols * dir->lines || c < dir->cols) {
  1623.                   if (c < dir->cols)
  1624.                      ++c;
  1625.                } else if (dir->vcols > 0 && offset < dir->vcols * dir->lines &&
  1626.                          c == dir->cols) {
  1627.                   offset += dir->lines;
  1628.                   recalculate_dir( dir, flist, offset );
  1629.                }
  1630.             }
  1631.             goodkey = TRUE;
  1632.             break;
  1633.          case CharLeft :
  1634.             if (offset == 0 || c > 1) {
  1635.                if (c > 1) {
  1636.                   change = TRUE;
  1637.                   --c;
  1638.                }
  1639.             } else if (dir->vcols > 0 && offset > 0 && c == 1) {
  1640.                change = TRUE;
  1641.  
  1642.                /*
  1643.                 * recalculate the dir display stuff.
  1644.                 */
  1645.                offset -= dir->lines;
  1646.                recalculate_dir( dir, flist, offset );
  1647.             }
  1648.             goodkey = TRUE;
  1649.             break;
  1650.          case CharRight :
  1651.             if (offset == dir->vcols * dir->lines || c < dir->cols) {
  1652.                if (c < dir->cols) {
  1653.                   change = TRUE;
  1654.                   ++c;
  1655.                   if (c == dir->cols) {
  1656.                      if ( r > dir->prow)
  1657.                         r = dir->prow;
  1658.                   }
  1659.                }
  1660.             } else if (dir->vcols > 0 && offset < dir->vcols * dir->lines &&
  1661.                          c == dir->cols) {
  1662.                change = TRUE;
  1663.                offset += dir->lines;
  1664.                recalculate_dir( dir, flist, offset );
  1665.                if (r > dir->prow)
  1666.                   r = dir->prow;
  1667.             }
  1668.             goodkey = TRUE;
  1669.             break;
  1670.          case BegOfLine :
  1671.             change = TRUE;
  1672.             c = r = 1;
  1673.             goodkey = TRUE;
  1674.             break;
  1675.          case EndOfLine :
  1676.             change = TRUE;
  1677.             r = dir->prow;
  1678.             c = dir->cols;
  1679.             goodkey = TRUE;
  1680.             break;
  1681.          case ScreenDown :
  1682.             change = TRUE;
  1683.             r = (c == dir->cols) ? r = dir->prow : dir->lines;
  1684.             goodkey = TRUE;
  1685.             break;
  1686.          case ScreenUp :
  1687.             change = TRUE;
  1688.             r = 1;
  1689.             goodkey = TRUE;
  1690.             break;
  1691.          default :
  1692.             break;
  1693.       }
  1694.       if (goodkey) {
  1695.          s_output( blank, dir->row+DIR4_ROW,
  1696.                    dir->col+strlen( dir4 )+3+stem_len, color );
  1697.          fno = offset + (c-1)*dir->lines + (r-1);
  1698.          s_output( flist[fno].fname, dir->row+DIR4_ROW,
  1699.                    dir->col+strlen( dir4 )+3+stem_len, color );
  1700.          my_ltoa( flist[fno].fsize, asize, 10 );
  1701.          s_output( blank, dir->row+DIR5_ROW, dir->col+strlen( dir5 )+3, color );
  1702.          s_output( asize, dir->row+DIR5_ROW, dir->col+strlen( dir5 )+3, color );
  1703.          xygoto( (c-1)*14+dir->col+2, r+dir->row+3 );
  1704.          goodkey = FALSE;
  1705.          if (change) {
  1706.             hlight_line( (oldc-1)*14+dir->col+2, oldr+dir->row+3, 12, color );
  1707.             hlight_line( (c-1)*14+dir->col+2, r+dir->row+3, 12, file_color );
  1708.             change = FALSE;
  1709.          }
  1710.       }
  1711.    }
  1712.    dir->select = fno;
  1713.    return( func == AbortCommand ? ERROR : OK );
  1714. }
  1715.  
  1716. #endif
  1717.  
  1718.  
  1719. /*
  1720.  * Name:    recalculate_dir
  1721.  * Purpose: To recalcute dir structure when cursor goes ahead or behind screen
  1722.  * Date:    November 13, 1993
  1723.  * Passed:  dir:    pointer to file structure
  1724.  *          flist:  pointer to file structure
  1725.  *          offset: number of files from beginning of flist
  1726.  * Notes:   Find new number of files on the screen.  Then, find out
  1727.  *           how many files names are in the last column.
  1728.  */
  1729. void recalculate_dir( DIRECTORY *dir , FTYPE *flist, int offset )
  1730. {
  1731. register int off;
  1732.  
  1733.    off = offset;
  1734.    dir->nfiles = (dir->cnt - off) > dir->avail ? dir->avail :
  1735.                 (dir->cnt - off);
  1736.    dir->prow = dir->lines - (dir->avail - dir->nfiles);
  1737.    write_directory_list( flist+off, *dir );
  1738. }
  1739.  
  1740.  
  1741. /*
  1742.  * Name:     shell_sort
  1743.  * Purpose:  To sort file names
  1744.  * Date:     February 13, 1992
  1745.  * Modified: November 13, 1993, Frank Davis per Byrial Jensen
  1746.  * Passed:   flist: pointer to file structure
  1747.  *           cnt:   number of files to sort
  1748.  * Notes:    this implementation of Shellsort is based on the one by Robert
  1749.  *            Sedgewick on page 109, _Algorithms in C_.
  1750.  *
  1751.  * Change:   Use of my_memcmp instead memcmp
  1752.  *           (In somes cases not-english people wants to use their own letters
  1753.  *           even in filenames; I am such one, Byrial).
  1754.  */
  1755. void shell_sort( FTYPE *flist, int cnt )
  1756. {
  1757. int  i;
  1758. register int j;
  1759. register int inc;
  1760. FTYPE temp;
  1761. FTYPE *fl;
  1762.  
  1763.    if (cnt > 1) {
  1764.  
  1765.       sort.order_array = (mode.search_case == IGNORE) ?
  1766.                               sort_order.ignore : sort_order.match;
  1767.  
  1768.       fl = flist;
  1769.  
  1770.       /*
  1771.        * figure the increments, per Donald Knuth, _Sorting and Searching_, and
  1772.        *  Robert Sedgewick, _Algorithms in C_.
  1773.        */
  1774.       j = cnt / 9;
  1775.       for (inc=1; inc <= j; inc = 3 * inc + 1);
  1776.  
  1777.       /*
  1778.        * now, Shellsort the directory file names.
  1779.        */
  1780.       for (; inc > 0; inc /= 3) {
  1781.          for (i=inc; i < cnt; i++) {
  1782.             j = i;
  1783.             memcpy( &temp, fl+j, sizeof(FTYPE) );
  1784. #if defined( __UNIX__ )
  1785.             while (j >= inc  &&  my_memcmp( (text_ptr)fl[j-inc].fname,
  1786.                                   (text_ptr)temp.fname, NAME_MAX+2 ) > 0) {
  1787.                memcpy( fl+j, fl+j-inc, sizeof(FTYPE) );
  1788.                j -= inc;
  1789.             }
  1790. #else
  1791.             while (j >= inc  &&  my_memcmp( (text_ptr)fl[j-inc].fname,
  1792.                                    (text_ptr)temp.fname, 14 ) > 0) {
  1793.                memcpy( fl+j, fl+j-inc, sizeof(FTYPE) );
  1794.                j -= inc;
  1795.             }
  1796. #endif
  1797.             memcpy( fl+j, &temp, sizeof(FTYPE) );
  1798.          }
  1799.       }
  1800.    }
  1801. }
  1802.