home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / progmisc / tde221.zip / DIRLIST.C < prev    next >
C/C++ Source or Header  |  1993-04-01  |  31KB  |  945 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, _Communications of the ACM_ 2 (No. 2): 30-32, 1959.
  23.  *
  24.  * See also:
  25.  *
  26.  *   Donald Ervin Knuth, _The Art of Computer Programming; Volume 3:  Sorting
  27.  *     and Searching_, Addison-Wesley, Reading, Mass., 1973, Chapter 5,
  28.  *     "Sorting", pp 84-95.  ISBN 0-201-03803-X.
  29.  *
  30.  *   Robert Sedgewick, _Algorithms in C_, Addison-Wesley, Reading, Mass.,
  31.  *     1990, Chapter 8, "Elementary Sorting Methods", pp 107-111.
  32.  *     ISBN 0-201-51425-7.
  33.  *
  34.  *
  35.  * New editor name:  TDE, the Thomson-Davis Editor.
  36.  * Author:           Frank Davis
  37.  * Date:             June 5, 1991, version 1.0
  38.  * Date:             July 29, 1991, version 1.1
  39.  * Date:             October 5, 1991, version 1.2
  40.  * Date:             January 20, 1992, version 1.3
  41.  * Date:             February 17, 1992, version 1.4
  42.  * Date:             April 1, 1992, version 1.5
  43.  * Date:             June 5, 1992, version 2.0
  44.  * Date:             October 31, 1992, version 2.1
  45.  * Date:             April 1, 1993, version 2.2
  46.  *
  47.  * This code is released into the public domain, Frank Davis.
  48.  *    You may distribute it freely.
  49.  */
  50.  
  51. #include "tdestr.h"
  52. #include "common.h"
  53. #include "define.h"
  54. #include "tdefunc.h"
  55.  
  56.  
  57. /*
  58.  * Name:    dir_help
  59.  * Purpose: To prompt the user and list the directory contents
  60.  * Date:    February 13, 1992
  61.  * Passed:  window:  pointer to current window
  62.  */
  63. int  dir_help( WINDOW *window )
  64. {
  65. char dname[MAX_COLS+2]; /* directory search pattern */
  66. char stem[MAX_COLS+2];  /* directory stem */
  67. char drive[_MAX_DRIVE]; /* splitpath drive buff */
  68. char dir[_MAX_DIR];     /* splitpath dir buff */
  69. char fname[_MAX_FNAME]; /* splitpath fname buff */
  70. char ext[_MAX_EXT];     /* splitpath ext buff */
  71. int  rc;
  72. int  file_mode;
  73. int  bin_length;
  74. int  prompt_line;
  75.  
  76.    if (window != NULL) {
  77.       entab_linebuff( );
  78.       if (un_copy_line( window->ll, window, TRUE ) == ERROR)
  79.          return( ERROR );
  80.       prompt_line = window->bottom_line;
  81.    } else
  82.       prompt_line = g_display.nlines;
  83.  
  84.    /*
  85.     * search path or pattern
  86.     */
  87.    dname[0] = '\0';
  88.    rc = get_name( dir1, prompt_line, dname, g_display.message_color );
  89.  
  90.    if (rc == OK) {
  91.       if (validate_path( dname, stem ) == OK) {
  92.          rc = list_and_pick( dname, stem, window );
  93.  
  94.          /*
  95.           * if everything is everything, load in the file selected by user.
  96.           */
  97.          if (rc == OK) {
  98.             file_mode = TEXT;
  99.             bin_length = 0;
  100.             _splitpath( dname, drive, dir, fname, ext );
  101.             if (stricmp( ext, ".exe" ) == 0  ||  stricmp( ext, ".com" ) == 0) {
  102.                file_mode = BINARY;
  103.                bin_length = g_status.file_chunk;
  104.             }
  105.             if (window != NULL)
  106.                attempt_edit_display( dname, LOCAL, file_mode, bin_length );
  107.             else
  108.                attempt_edit_display( dname, GLOBAL, file_mode, bin_length );
  109.          }
  110.       } else
  111.          /*
  112.           * invalid path or file name
  113.           */
  114.          error( WARNING,
  115.                 window != NULL ? window->bottom_line : g_display.nlines, dir2 );
  116.    }
  117.    return( rc );
  118. }
  119.  
  120.  
  121. /*
  122.  * Name:    validate_path
  123.  * Purpose: make sure we got a valid search pattern or subdirectory
  124.  * Date:    February 13, 1992
  125.  * Passed:  dname: search path entered by user
  126.  *          stem:  directory stem is returned
  127.  * Returns: successful or not
  128.  * Notes:   we need to validate the search path or pattern.  if the search
  129.  *            pattern is valid, then we need to get the search stem.
  130.  *          the user may enter a subdirectory or some kind of search pattern.
  131.  *             if the user enters a subdirectory, then there are a few things
  132.  *             we need to take care of  1) find out if the subdirectory is
  133.  *             the root, 2) append a '\' to the subdirectory so we can create
  134.  *             a search pattern for the subdirectory, 3) don't append '\' to
  135.  *             the root, it already has a '\'.
  136.  *          if the user enters a search pattern, then we need to dissect the
  137.  *             search path.  we must create a stem from the search pattern.
  138.  */
  139. int  validate_path( char *dname, char *stem )
  140. {
  141. int  rc;
  142. DTA  dta;               /* temp disk transfer struct for findfirst, etc. */
  143. int  fattr;
  144. int  i;
  145. int  len;
  146. char *p;
  147. char temp[MAX_COLS+2];  /* directory stem */
  148.  
  149.    /*
  150.     * if path name is void then the current working directory is implied.
  151.     */
  152.    if (dname[0] == '\0') {
  153.  
  154.       assert( strlen( stardotstar ) < MAX_COLS );
  155.  
  156.       strcpy( dname, stardotstar );
  157.       stem[0] = '\0';
  158.       rc = OK;
  159.    } else {
  160.  
  161.       /*
  162.        * get the attributes of the search pattern, so we can determine if
  163.        * this is a pattern or subdirectory.
  164.        */
  165.       rc = get_fattr( dname, &fattr );
  166.  
  167.       if (rc == OK && (fattr & SUBDIRECTORY)) {
  168.          assert( strlen( dname ) < MAX_COLS );
  169.          strcpy( stem, dname );
  170.  
  171.          /*
  172.           * if this is the root directory ( \ ), don't append '\' to it.
  173.           * user entered a subdirectory - append *.* to get contents of
  174.           * subdirectory.
  175.           */
  176.          len = strlen( stem );
  177.          if (stem[len-1] != '\\') {
  178.             strcat( stem, "\\" );
  179.             strcat( dname, "\\" );
  180.          }
  181.          strcat( dname, stardotstar );
  182.  
  183.       /*
  184.        * not a subdirectory.  let's see if any files match the search
  185.        * pattern.
  186.        */
  187.       } else if (rc != ERROR) {
  188.          if ((rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN |
  189.                               SYSTEM | SUBDIRECTORY | ARCHIVE )) == OK) {
  190.  
  191.             /*
  192.              * copy dname to "temp" so we can use "temp" to find the stem.
  193.              *    we need to search the pattern backwards to figure the stem.
  194.              */
  195.  
  196.             assert( strlen( dname ) < MAX_COLS );
  197.  
  198.             strcpy( temp, dname );
  199.             len = strlen( dname );
  200.             for (i=len,p=temp+len; i>=0; i--) {
  201.                /*
  202.                 *  if we run into the '\' or the ':', then we got a stem.
  203.                 */
  204.                if (*p == '\\' || *p == ':') {
  205.                   p = temp + i;
  206.                   *(p+1) = '\0';
  207.                   break;
  208.                /*
  209.                 * if we're at the beginning of the string, stem == '\0'
  210.                 */
  211.                } else if (i == 0) {
  212.                   *p = '\0';
  213.                   break;
  214.                }
  215.                --p;
  216.             }
  217.  
  218.             assert( strlen( temp ) < MAX_COLS );
  219.  
  220.             strcpy( stem, temp );
  221.          } else
  222.             rc = ERROR;
  223.  
  224.       /*
  225.        * user did not enter a valid subdirectory name or search pattern.
  226.        */
  227.       } else
  228.          rc = ERROR;
  229.    }
  230.    return( rc );
  231. }
  232.  
  233.  
  234. /*
  235.  * Name:    list_and_pick
  236.  * Purpose: To show matching file names and let user pick a file
  237.  * Date:    February 13, 1992
  238.  * Passed:  dname:  directory search pattern
  239.  *          stem:   stem of directory search pattern
  240.  *          window:  pointer to current window
  241.  * Returns: return code from pick.  rc = OK, then edit a new file.
  242.  * Notes:   real work routine of this function.  save the cwd and let the
  243.  *           user search upwards or downwards thru the directory structure.
  244.  *          since we are doing DOS directory functions, we need to check the
  245.  *           return code after each DOS call for critical errors.
  246.  */
  247. int  list_and_pick( char *dname, char *stem, WINDOW *window )
  248. {
  249. int  rc;
  250. DTA  dta;               /* disk transfer address for findfirst */
  251. DIRECTORY dir;          /* contains all info for dir display */
  252. unsigned int cnt;       /* number of matching files */
  253. FTYPE *flist, *p;       /* pointer to list of matching files */
  254. char cwd[MAX_COLS];     /* save the current working directory in this buff */
  255. char dbuff[MAX_COLS];   /* temporary directory buff */
  256. char prefix[MAX_COLS];  /* directory prefix  */
  257. int  change_directory = FALSE;
  258. int  stop;
  259. int  len;
  260. int  drive;
  261.  
  262.    /*
  263.     * Some algorithms alloc the maximum possible number of files in
  264.     *  a directory, eg. 256 or 512.  Let's count the number of matching
  265.     *  files so we know egxactly how much memory to request from calloc.
  266.     *  Depending on the op system, disk media, disk format, or version of DOS,
  267.     *  the max number of files may vary, anyway, also, additionally.
  268.     */
  269.    rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN | SYSTEM |
  270.                                 SUBDIRECTORY | ARCHIVE );
  271.    if (rc != ERROR) {
  272.       for (cnt=1; (rc = my_findnext( &dta )) == OK;)
  273.          ++cnt;
  274.       flist = (FTYPE *)calloc( cnt, sizeof(FTYPE) );
  275.    } else
  276.       flist = NULL;
  277.    if (rc != ERROR && flist != NULL) {
  278.  
  279.       stop = FALSE;
  280.       /*
  281.        * If user entered drive name in search pattern, find out the drive and
  282.        *  directory stem.
  283.        */
  284.       if (stem[1] == ':') {
  285.  
  286.          /*
  287.           * If the second character of the search pattern is a ':', the
  288.           *  the first character of the pattern should be the drive.
  289.           *  Convert drive to lower case and get a numerical representation.
  290.           * CAVEAT:  In DOS v 2.x, there may be up to 63 logical drives.
  291.           *   my algorithm may blow up if the number of logical drives
  292.           *   is greater than 'Z'.
  293.           * For DOS >= 3, the number of drives is limited to 26, I think.
  294.           */
  295.          drive = stem[0];
  296.          if (drive < 'a')
  297.             drive += 32;
  298.          drive = drive - 'a' + 1;
  299.          rc = get_current_directory( dbuff, drive );
  300.          if (rc == ERROR)
  301.             stop = TRUE;
  302.          else {
  303.  
  304.             /*
  305.              * Put drive letter, ':', and '\' in front of current directory.
  306.              */
  307.             prefix[0] = (char)(drive - 1 + 'a');
  308.             prefix[1] = ':';
  309.             prefix[2] = '\\';
  310.             prefix[3] = '\0';
  311.             assert( strlen( prefix ) + strlen( dbuff ) < MAX_COLS );
  312.             strcpy( cwd, prefix );
  313.             strcat( cwd, dbuff );
  314.          }
  315.  
  316.       /*
  317.        * else get current directory from default drive
  318.        */
  319.       } else {
  320.  
  321.          /*
  322.           * 0 = default drive.
  323.           */
  324.          drive = 0;
  325.          rc = get_current_directory( dbuff, drive );
  326.          if (rc == ERROR)
  327.             stop = TRUE;
  328.          else {
  329.  
  330.             /*
  331.              * Put a '\' in front of the current directory.
  332.              */
  333.             prefix[0] = '\\';
  334.             prefix[1] = '\0';
  335.  
  336.             assert( strlen( prefix ) + strlen( dbuff ) < MAX_COLS );
  337.  
  338.             strcpy( cwd, prefix );
  339.             strcat( cwd, dbuff );
  340.          }
  341.       }
  342.  
  343.       while (stop == FALSE) {
  344.          /*
  345.           * If we had enough memory, find all matching file names.  Append
  346.           *  '\\' at the end of subdirectory names so user will know if
  347.           *  name is a directory.  Might as well find everything, because
  348.           *  i also forget subdirectory names, too.
  349.           *
  350.           * when we get here, we have already done: 1) my_findfirst and
  351.           *  my_findnext, 2) counted the number of matching files, and
  352.           *  3) allocated space.
  353.           */
  354.          p = flist;
  355.          cnt = 0;
  356.  
  357.          rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN | SYSTEM |
  358.                                  SUBDIRECTORY | ARCHIVE );
  359.          if (rc != ERROR) {
  360.  
  361.             /*
  362.              * p is pointer that walks down the file info structure.
  363.              *  save the file name, file size, and directory character,
  364.              *  if needed, for each matching file we find.
  365.              */
  366.  
  367.             assert( strlen( dta.name ) < 14 );
  368.  
  369.             strcpy( p->fname, dta.name );
  370.             p->fsize = dta.size;
  371.             if (dta.attrib & SUBDIRECTORY)
  372.                strcat( p->fname, "\\" );
  373.             for (cnt=1; (rc = my_findnext( &dta )) == OK; ) {
  374.                ++p;
  375.  
  376.                assert( strlen( dta.name ) < 14 );
  377.  
  378.                strcpy( p->fname, dta.name );
  379.                p->fsize = dta.size;
  380.                if (dta.attrib & SUBDIRECTORY)
  381.                   strcat( p->fname, "\\" );
  382.                cnt++;
  383.             }
  384.          }
  385.  
  386.          if (rc != ERROR) {
  387.             shell_sort( flist, cnt );
  388.  
  389.             /*
  390.              * figure out number of rows, cols, etc... then display dir list
  391.              */
  392.             setup_directory_window( &dir, cnt );
  393.             write_directory_list( flist, dir );
  394.  
  395.             /*
  396.              * Let user select file name or another search directory.
  397.              *  Save the choice in dbuff.  rc == OK if user selected file or dir.
  398.              */
  399.             rc = select_file( flist, stem, &dir );
  400.  
  401.             assert( strlen( flist[dir.select].fname ) < MAX_COLS );
  402.  
  403.             strcpy( dbuff, flist[dir.select].fname );
  404.          }
  405.  
  406.          /*
  407.           *  give memory back.
  408.           */
  409.          free( flist );
  410.  
  411.          if (rc == ERROR)
  412.             stop = TRUE;
  413.          else {
  414.             len = strlen( dbuff );
  415.  
  416.             /*
  417.              * If the last character in a file name is '\' then let's
  418.              *  do a dir on selected directory.  See the matching
  419.              *  else when the user selects a file.
  420.              */
  421.             if (dbuff[len-1] == '\\') {
  422.  
  423.                /*
  424.                 * Stem has subdirectory path.  dbuff has selected path.
  425.                 * Create a new dname with stem and dbuff.
  426.                 */
  427.  
  428.                assert( strlen( stem ) + strlen( dbuff ) < MAX_COLS );
  429.  
  430.                strcpy( dname, stem );
  431.                strcat( dname, dbuff );
  432.                len = strlen( dname );
  433.                strcpy( dbuff, dname );
  434.  
  435.                /*
  436.                 * The last character in dbuff is '\', because we append the
  437.                 *  '\' to every directory entry in the file list.  Replace
  438.                 *  it with a NULL char then we will have a valid path name.
  439.                 */
  440.                dbuff[len-1] = '\0';
  441.  
  442.                /*
  443.                 * now let's change to the selected subdirectory.
  444.                 */
  445.                rc = set_current_directory( dbuff );
  446.                if (rc == OK) {
  447.  
  448.                   /*
  449.                    * Every time we change directories, we need to get the
  450.                    *  current directory so we will be sure to have the
  451.                    *  correct path.
  452.                    */
  453.                   rc = get_current_directory( dbuff, drive );
  454.                   if (rc == OK) {
  455.  
  456.                      assert( strlen( prefix ) + strlen( dbuff ) < MAX_COLS );
  457.  
  458.                      strcpy( dname, prefix );
  459.                      strcat( dname, dbuff );
  460.                      change_directory = TRUE;
  461.                   }
  462.                }
  463.  
  464.                /*
  465.                 * Validate the new path and allocate memory for the
  466.                 *  matching files.
  467.                 */
  468.                if (rc == OK)
  469.                   rc = validate_path( dname, stem );
  470.                if (rc == OK) {
  471.                   rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN |
  472.                                   SYSTEM | SUBDIRECTORY | ARCHIVE );
  473.                   if (rc != ERROR) {
  474.                      for (cnt=1; (rc = my_findnext( &dta )) == OK;)
  475.                         ++cnt;
  476.                      flist = (FTYPE *)calloc( cnt, sizeof(FTYPE) );
  477.                   }
  478.                }
  479.                if (flist == NULL || rc == ERROR) {
  480.                   stop = TRUE;
  481.                   rc = ERROR;
  482.                }
  483.             } else {
  484.  
  485.                /*
  486.                 * user selected a file.  store fname in dname and return.
  487.                 */
  488.                rc = OK;
  489.                stop = TRUE;
  490.  
  491.                assert( strlen( stem ) + strlen( dbuff ) < MAX_COLS );
  492.  
  493.                strcpy( dname, stem );
  494.                strcat( dname, dbuff );
  495.             }
  496.          }
  497.       }
  498.  
  499.       /*
  500.        * Go back to the current directory if needed.
  501.        */
  502.       if (change_directory)
  503.          set_current_directory( cwd );
  504.       if (window != NULL)
  505.          redraw_screen( window );
  506.    } else {
  507.       /*
  508.        * out of memory
  509.        */
  510.       error( WARNING,  window != NULL ? window->bottom_line : g_display.nlines,
  511.              dir3 );
  512.       rc = ERROR;
  513.    }
  514.    return( rc );
  515. }
  516.  
  517.  
  518. /*
  519.  * Name:    setup_directory_window
  520.  * Purpose: set number of rows and cols in directory window
  521.  * Date:    February 13, 1992
  522.  * Passed:  dir: pointer to directory structure
  523.  *          cnt: number of files
  524.  * Notes:   set up stuff we need to know about how to display files.
  525.  */
  526. void setup_directory_window( DIRECTORY *dir, int cnt )
  527. {
  528. int  i;
  529. int  wid;
  530. char temp[MAX_COLS];    /* line to output */
  531.  
  532.    /*
  533.     * setup the fixed vars used in dir display.
  534.     *    dir->col =      physical upper left column of dir screen
  535.     *    dir->row =      physical upper left row or line of dir screen
  536.     *    dir->wid =      width of physical screen
  537.     *    dir->hgt =      height of physical screen
  538.     *    dir->max_cols   number of columns of files in dir screen
  539.     *    dir->max_lines  number of lines of files in each column in dir screen
  540.     *    dir->cnt        number of files in list
  541.     */
  542.    dir->col = 3;
  543.    dir->row = 5;
  544.    wid = dir->wid = 72;
  545.    dir->hgt = 16;
  546.    dir->max_cols = 5;
  547.    dir->max_lines = 9;
  548.    dir->cnt = cnt;
  549.  
  550.    /*
  551.     * Find out how many lines in each column are needed to display
  552.     *  matching files.
  553.     */
  554.    dir->lines = dir->cnt / dir->max_cols + (dir->cnt % dir->max_cols ? 1 : 0);
  555.    if (dir->lines > dir->max_lines)
  556.       dir->lines = dir->max_lines;
  557.  
  558.    /*
  559.     * Find out how many columns of file names we need.
  560.     */
  561.    dir->cols = dir->cnt / dir->lines + (dir->cnt % dir->lines ? 1 : 0);
  562.    if (dir->cols > dir->max_cols)
  563.       dir->cols = dir->max_cols;
  564.  
  565.  
  566.    /*
  567.     * Find the maximun number of file names we can display in help screen.
  568.     */
  569.    dir->avail = dir->lines * dir->cols;
  570.  
  571.    /*
  572.     * Now find the number of file names we do have on the screen.  Every
  573.     *  time we slide the "window", we have to calculate a new nfiles.
  574.     */
  575.    dir->nfiles = dir->cnt > dir->avail ? dir->avail : dir->cnt;
  576.  
  577.    /*
  578.     * A lot of times, the number of matching files will not fit evenly
  579.     *  in our help screen.  The last column on the right will be partially
  580.     *  filled, hence the variable name prow (partial row).  When there are
  581.     *  more file names than can fit on the screen, we have to calculate
  582.     *  prow every time we slide the "window" of files.
  583.     */
  584.    dir->prow = dir->lines - (dir->avail - dir->nfiles);
  585.  
  586.    /*
  587.     * Find out how many "virtual" columns of file names we have.  If
  588.     *  all the files can fit in the dir screen, there will be no
  589.     *  virtual columns.
  590.     */
  591.    if (dir->cnt < dir->avail)
  592.       dir->vcols = 0;
  593.    else
  594.       dir->vcols =  (dir->cnt - dir->avail) / dir->max_lines +
  595.                    ((dir->cnt - dir->avail) % dir->max_lines ? 1 : 0);
  596.  
  597.    /*
  598.     * Find the physical display column in dir screen.
  599.     */
  600.    dir->flist_col[0] = dir->col + 2;
  601.    for (i=1; i<dir->max_cols; i++)
  602.       dir->flist_col[i] = dir->flist_col[i-1] + 14;
  603.  
  604.    /*
  605.     * Now, draw the borders of the dir screen.
  606.     */
  607.    for (i=0; i < dir->hgt; i++) {
  608.       if (i == 0 || i == dir->hgt-1) {
  609.          memset( temp, '─', wid );
  610.          temp[wid] = '\0';
  611.          if (i == 0) {
  612.             temp[0] = '┌';
  613.             temp[wid-1] = '┐';
  614.          } else {
  615.             temp[0] = '└';
  616.             temp[wid-1] = '┘';
  617.          }
  618.       } else {
  619.          memset( temp, ' ', wid );
  620.          temp[wid] = '\0';
  621.          temp[0] = temp[wid-1] = '│';
  622.       }
  623.       s_output( temp, dir->row+i, dir->col, g_display.help_color );
  624.    }
  625.  
  626.    /*
  627.     * Write headings in help screen.
  628.     */
  629.    s_output( dir4, dir->row+1, dir->col+3, g_display.help_color );
  630.    s_output( dir5, dir->row+2, dir->col+3, g_display.help_color );
  631.    s_output( dir6, dir->row+2, dir->col+44, g_display.help_color );
  632.    s_output( dir7, dir->row+14, dir->col+8, g_display.help_color );
  633. }
  634.  
  635.  
  636. /*
  637.  * Name:    write_directory_list
  638.  * Purpose: given directory list, display matching files
  639.  * Date:    February 13, 1992
  640.  * Passed:  flist: pointer to list of files
  641.  *          dir:   directory display structure
  642.  * Notes:   blank out the previous file name and display the new one.
  643.  */
  644. void write_directory_list( FTYPE *flist, DIRECTORY dir )
  645. {
  646. FTYPE *p, *top;
  647. int  i;
  648. int  j;
  649. int  k;
  650. int  end;
  651. int  line;
  652. int  col;
  653. int  color;
  654.  
  655.    color = g_display.help_color;
  656.    top = flist;
  657.    for (i=0; i < dir.lines; ++i) {
  658.       p = top;
  659.       end = FALSE;
  660.       for (j=0; j < dir.cols; ++j) {
  661.          col = dir.flist_col[j];
  662.          line = i + dir.row + 4;
  663.  
  664.          /*
  665.           * We need to blank out all lines and columns used to display
  666.           *  files, because there may be some residue from a previous dir
  667.           */
  668.          s_output( "            ", line, col, color );
  669.          if (!end) {
  670.             s_output( p->fname, line, col, color );
  671.             p += dir.lines;
  672.             k = p - flist;
  673.             if (k >= dir.nfiles)
  674.                end = TRUE;
  675.          }
  676.       }
  677.       ++top;
  678.    }
  679. }
  680.  
  681.  
  682. /*
  683.  * Name:    select_file
  684.  * Purpose: To let user select a file from dir list
  685.  * Date:    February 13, 1992
  686.  * Passed:  flist: pointer to list of files
  687.  *          stem:  base directory
  688.  *          dir:   directory display stuff
  689.  * Notes:   let user move thru the file names with the cursor keys
  690.  */
  691. int  select_file( FTYPE *flist, char *stem, DIRECTORY *dir )
  692. {
  693. int  ch;                /* input character from user */
  694. int  func;              /* function of character input by user */
  695. int  fno;               /* index into flist of the file under cursor */
  696. int  goodkey;           /* is key a recognized function key? */
  697. int  r;                 /* current row of cursor */
  698. int  c;                 /* current column of cursor */
  699. int  offset;            /* offset into file list */
  700. int  stop;              /* stop indicator */
  701. int  stem_len;          /* stem length */
  702. int  color;             /* color of help screen */
  703. int  file_color;        /* color of current file */
  704. int  change;            /* boolean, hilite another file? */
  705. int  oldr;              /* old row */
  706. int  oldc;              /* old column */
  707. char asize[20];         /* ascii file size */
  708. char blank[20];         /* blank out file names */
  709.  
  710.    /*
  711.     * initial everything.
  712.     */
  713.    memset( blank, ' ', 12 );
  714.    blank[12] = '\0';
  715.    c = r = 1;
  716.    ch = fno = offset = 0;
  717.    color = g_display.help_color;
  718.    file_color = g_display.hilited_file;
  719.    goodkey = TRUE;
  720.    stop = FALSE;
  721.    stem_len = strlen( stem );
  722.    s_output( stem, dir->row+1, dir->col+19, color );
  723.    s_output( flist[fno].fname, dir->row+1, dir->col+19+stem_len, color );
  724.    ltoa( flist[fno].fsize, asize, 10 );
  725.    s_output( blank, dir->row+2, dir->col+19, color );
  726.    s_output( asize, dir->row+2, dir->col+19, color );
  727.    itoa( dir->cnt,  asize, 10 );
  728.    s_output( blank, dir->row+2, dir->col+57, color );
  729.    s_output( asize, dir->row+2, dir->col+57, color );
  730.    xygoto( (c-1)*14+dir->col+2, r+dir->row+3 );
  731.    hlight_line( (c-1)*14+dir->col+2, r+dir->row+3, 12, file_color );
  732.    change = FALSE;
  733.    while (stop == FALSE) {
  734.       oldr = r;
  735.       oldc = c;
  736.       ch = getkey( );
  737.       func = getfunc( ch );
  738.  
  739.       /*
  740.        * User may have redefined the Enter and ESC keys.  Make the Enter key
  741.        *  perform a Rturn in this function. Make the ESC key do an AbortCommand.
  742.        */
  743.       if (ch == RTURN)
  744.          func = Rturn;
  745.       else if (ch == ESC)
  746.          func = AbortCommand;
  747.  
  748.       switch (func) {
  749.          case Rturn       :
  750.          case NextLine    :
  751.          case BegNextLine :
  752.             stop = TRUE;
  753.             break;
  754.          case AbortCommand :
  755.             stop = TRUE;
  756.             break;
  757.          case LineUp :
  758.             if (r > 1) {
  759.                change = TRUE;
  760.                --r;
  761.             } else {
  762.                r = dir->lines;
  763.                change = TRUE;
  764.                if (offset == 0 || c > 1) {
  765.                   if (c > 1)
  766.                      --c;
  767.                } else if (dir->vcols > 0 && offset > 0 && c == 1) {
  768.                   /*
  769.                    * recalculate the dir display stuff.
  770.                    */
  771.                   offset -= dir->lines;
  772.                   recalculate_dir( dir, flist, offset );
  773.                }
  774.             }
  775.             goodkey = TRUE;
  776.             break;
  777.          case LineDown :
  778.             if (r < dir->prow) {
  779.                change = TRUE;
  780.                ++r;
  781.             } else if (r < dir->lines && c != dir->cols) {
  782.                change = TRUE;
  783.                ++r;
  784.             } else {
  785.                change = TRUE;
  786.                r = 1;
  787.                if (offset == dir->vcols * dir->lines || c < dir->cols) {
  788.                   if (c < dir->cols)
  789.                      ++c;
  790.                } else if (dir->vcols > 0 && offset < dir->vcols * dir->lines &&
  791.                          c == dir->cols) {
  792.                   offset += dir->lines;
  793.                   recalculate_dir( dir, flist, offset );
  794.                }
  795.             }
  796.             goodkey = TRUE;
  797.             break;
  798.          case CharLeft :
  799.             if (offset == 0 || c > 1) {
  800.                if (c > 1) {
  801.                   change = TRUE;
  802.                   --c;
  803.                }
  804.             } else if (dir->vcols > 0 && offset > 0 && c == 1) {
  805.                change = TRUE;
  806.  
  807.                /*
  808.                 * recalculate the dir display stuff.
  809.                 */
  810.                offset -= dir->lines;
  811.                recalculate_dir( dir, flist, offset );
  812.             }
  813.             goodkey = TRUE;
  814.             break;
  815.          case CharRight :
  816.             if (offset == dir->vcols * dir->lines || c < dir->cols) {
  817.                if (c < dir->cols) {
  818.                   change = TRUE;
  819.                   ++c;
  820.                   if (c == dir->cols) {
  821.                      if ( r > dir->prow)
  822.                         r = dir->prow;
  823.                   }
  824.                }
  825.             } else if (dir->vcols > 0 && offset < dir->vcols * dir->lines &&
  826.                          c == dir->cols) {
  827.                change = TRUE;
  828.                offset += dir->lines;
  829.                recalculate_dir( dir, flist, offset );
  830.                if (r > dir->prow)
  831.                   r = dir->prow;
  832.             }
  833.             goodkey = TRUE;
  834.             break;
  835.          case BegOfLine :
  836.             change = TRUE;
  837.             c = r = 1;
  838.             goodkey = TRUE;
  839.             break;
  840.          case EndOfLine :
  841.             change = TRUE;
  842.             r = dir->prow;
  843.             c = dir->cols;
  844.             goodkey = TRUE;
  845.             break;
  846.          case ScreenDown :
  847.             change = TRUE;
  848.             r = (c == dir->cols) ? r = dir->prow : dir->lines;
  849.             goodkey = TRUE;
  850.             break;
  851.          case ScreenUp :
  852.             change = TRUE;
  853.             r = 1;
  854.             goodkey = TRUE;
  855.             break;
  856.          default :
  857.             break;
  858.       }
  859.       if (goodkey) {
  860.          s_output( blank, dir->row+1, dir->col+19+stem_len, color );
  861.          fno = offset + (c-1)*dir->lines + (r-1);
  862.          s_output( flist[fno].fname, dir->row+1, dir->col+19+stem_len, color );
  863.          ltoa( flist[fno].fsize, asize, 10 );
  864.          s_output( blank, dir->row+2, dir->col+19, color );
  865.          s_output( asize, dir->row+2, dir->col+19, color );
  866.          xygoto( (c-1)*14+dir->col+2, r+dir->row+3 );
  867.          goodkey = FALSE;
  868.          if (change) {
  869.             hlight_line( (oldc-1)*14+dir->col+2, oldr+dir->row+3, 12, color );
  870.             hlight_line( (c-1)*14+dir->col+2, r+dir->row+3, 12, file_color );
  871.             change = FALSE;
  872.          }
  873.       }
  874.    }
  875.    dir->select = fno;
  876.    return( func == AbortCommand ? ERROR : OK );
  877. }
  878.  
  879.  
  880. /*
  881.  * Name:    recalculate_dir
  882.  * Purpose: To recalcute dir structure when cursor goes ahead or behind screen
  883.  * Date:    February 13, 1992
  884.  * Passed:  dir:    pointer to file structure
  885.  *          flist:  pointer to file structure
  886.  *          offset: number of files from beginning of flist
  887.  * Notes:   Find new number of files on the screen.  Then, find out
  888.  *           how many files names are in the last column.
  889.  */
  890. void recalculate_dir( DIRECTORY *dir , FTYPE *flist, int offset )
  891. {
  892. register int off;
  893.  
  894.    off = offset;
  895.    dir->nfiles = (dir->cnt - off) > dir->avail ? dir->avail :
  896.                 (dir->cnt - off);
  897.    dir->prow = dir->lines - (dir->avail - dir->nfiles);
  898.    write_directory_list( flist+off, *dir );
  899. }
  900.  
  901.  
  902. /*
  903.  * Name:    shell_sort
  904.  * Purpose: To sort file names
  905.  * Date:    February 13, 1992
  906.  * Passed:  flist: pointer to file structure
  907.  *          cnt:   number of files to sort
  908.  * Notes:   this implementation of Shellsort is based on the one by Robert
  909.  *           Sedgewick on page 109, _Algorithms in C_.
  910.  */
  911. void shell_sort( FTYPE *flist, int cnt )
  912. {
  913. int  i;
  914. register int j;
  915. register int inc;
  916. FTYPE temp;
  917. FTYPE *fl;
  918.  
  919.    if (cnt > 1) {
  920.       fl = flist;
  921.  
  922.       /*
  923.        * figure the increments, per Donald Knuth, _Sorting and Searching_, and
  924.        *  Robert Sedgewick, _Algorithms in C_.
  925.        */
  926.       j = cnt / 9;
  927.       for (inc=1; inc <= j; inc = 3 * inc + 1);
  928.  
  929.       /*
  930.        * now, Shellsort the directory file names.
  931.        */
  932.       for (; inc > 0; inc /= 3) {
  933.          for (i=inc; i < cnt; i++) {
  934.             j = i;
  935.             memcpy( &temp, fl+j, sizeof(FTYPE) );
  936.             while (j >= inc && memcmp( fl[j-inc].fname, temp.fname, 14 ) > 0) {
  937.                memcpy( fl+j, fl+j-inc, sizeof(FTYPE) );
  938.                j -= inc;
  939.             }
  940.             memcpy( fl+j, &temp, sizeof(FTYPE) );
  941.          }
  942.       }
  943.    }
  944. }
  945.