home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / flistfrontend / src / dds.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-11-15  |  18.9 KB  |  767 lines

  1. #ifndef NO_IDENT
  2. static char *Id = "$Id: dds.c,v 1.17 1995/11/15 20:45:48 tom Exp $";
  3. #endif
  4.  
  5. /*
  6.  * Title:    dds.c - "FLIST" display routines
  7.  * Author:    T.E.Dickey
  8.  * Created:    03 May 1984
  9.  * Last update:
  10.  *        15 Nov 1995, size/allocated column-widths
  11.  *        26 Oct 1995, mods to make 'dds_while()' animated
  12.  *        18 Mar 1995, prototyped
  13.  *        18 Feb 1995, renamed 'beep' to avoid conflict with curses.
  14.  *        23 Feb 1989, use 'flist_chdir()'
  15.  *        25 Aug 1985, added 'dds_width' entrypoint.
  16.  *        24 Aug 1985, added 'dds_add2' entrypoint.
  17.  *        20 Aug 1985, call 'dirent_width' in 'dds_add'
  18.  *        30 Jul 1985, make 'dds_pack' adjust fixed-point if it lies off
  19.  *                 screen.
  20.  *        16 Jul 1985, use 'dirdata_add' in 'dds_add'.
  21.  *        03 Jul 1985, cleanup of 'filelist' usage.
  22.  *        11 Jun 1985, added cli-argument to dds_spawn
  23.  *        22 Apr 1985, 'dds_cmd' code is now done in 'edtcmd'.
  24.  *        02 Feb 1985, do 'crt_quit' before spawning to reset scroll-margin
  25.  *        10 Jan 1985, added call to 'crt_reset' to fix up after spawning.
  26.  *        29 Dec 1984, added 'dds_inx1', 'dds_inx2'.  Highlight name if
  27.  *                 line is selected.
  28.  *        21 Dec 1984, use 'flist_sysmsg'
  29.  *        10 Dec 1984, use 'dirent_chk2' instead of 'dirent_one'
  30.  *        09 Sep 1984, use 'multi_quit' in 'dds_last'
  31.  *        30 Aug 1984, added 'dds_last'
  32.  *        29 Aug 1984, plugged hole in 'dds_index' (numfile==0)
  33.  *        25 Aug 1984, cleanup buffer sizes
  34.  *        14 Aug 1984, use 'dirent_ccol()'
  35.  *        07 Aug 1984, cleanup of scrolling, declarations
  36.  *        14 Jul 1984, added completion-routine to dds_spawn
  37.  *        11 Jul 1984, added 'dds_hold' to spawn-process
  38.  *        03 Jul 1984, changed 'conv_filent' to 'dirent_conv'
  39.  *        19 Jun 1984
  40.  *        29 May 1984, to chop into 'crt' and 'dds' modules
  41.  *
  42.  * Function:    These routines perform screen display/refresh operations for
  43.  *        "FLIST".
  44.  *
  45.  * Entry:
  46.  *        dds_add:    Add a new FILENT-block to 'filelist[]' (show it)
  47.  *        dds_add2:    Add a new FILENT block at specific index
  48.  *        dds_all:    Display all lines in the current screen
  49.  *        dds_ast1:    Obtain completion status of spawned tasks
  50.  *        dds_fast:    Returns index to top, middle or bottom of screen
  51.  *        dds_hold:    Prompt for CR before continuing
  52.  *        dds_index:    Set cursor to given 'filelist[]' entry
  53.  *        dds_inx1:    Given 'filelist[]' index, return nth-file number
  54.  *        dds_inx2:    Given nth-file number, return 'filelist[]' index
  55.  *        dds_last:    Cleanup after command execution
  56.  *        dds_line:    Display a given entry of 'filelist[]'
  57.  *        dds_move:    Move to new index in 'filelist[]', may scroll
  58.  *        dds_pack:    Pack deleted entries out of 'filelist[]'
  59.  *        dds_scroll:    Do scrolling to given 'filelist[]' index
  60.  *        dds_spawn:    Spawn an external process, refreshing screen
  61.  *        dds_tell:    Update message/summary line (bottom)
  62.  *        dds_while:    Show "Working..." messages
  63.  *        dds_width:    Update display if (possible) column-width change
  64.  */
  65.  
  66. #include    <stdlib.h>
  67. #include    <stdio.h>
  68. #include    <ctype.h>
  69. #include    <types.h>
  70. #include    <time.h>
  71. #include    <string.h>
  72.  
  73. #include    <lib$routines.h>
  74. #include    <rms.h>
  75. #include    <ssdef.h>
  76. #include    <stsdef.h>
  77. #include    <descrip.h>
  78.  
  79. #include    "flist.h"
  80. #include    "canopen.h"
  81. #include    "dds.h"
  82. #include    "dirent.h"
  83. #include    "dirdata.h"
  84. #include    "dircmd.h"
  85. #include    "getpad.h"
  86.  
  87. #include    "strutils.h"
  88.  
  89. import(filelist); import(numfiles); import(numdlets);
  90. import(ccolumns); import(pcolumns); import(conv_list);
  91.  
  92. extern    int    multi_quit;    /* >0 iff multi-level quit in progress    */
  93.  
  94. #define    TOP_END    top_line = crt_top(), end_line = crt_end(), lpp = crt_lpp()
  95. #define    lpp2    (lpp-2)
  96. #define    lpp1    (lpp-1)
  97.  
  98. /* <dds_inx1>:
  99.  * Given an index into 'filelist[]', return the number in the range
  100.  * [1..(numfiles-numdlets)] which shows its real position in the display
  101.  * list.  This is used at the conclusion of each command to show the
  102.  * current position in the display list.
  103.  */
  104. int    dds_inx1 (int ifile)
  105. {
  106.     register
  107.     int    j,k;
  108.  
  109.     ifile++;        /* put into range [1..numfiles]    */
  110.     ifile = max(1,min(ifile,numfiles));
  111.     j = k = ifile;
  112.  
  113.     if (numdlets > 0)
  114.     {
  115.         while (--k >= 0)
  116.             if (DELETED(k))    j--;
  117.     }
  118.     return (j);
  119. }
  120.  
  121. /* <dds_inx2>:
  122.  * Given a position-index in the display list, return the corresponding
  123.  * index to 'filelist[]'.  This is used in scrolling commands by index
  124.  * number.
  125.  */
  126. int    dds_inx2 (int inx)
  127. {
  128.     register
  129.     int    j = numfiles-numdlets;
  130.  
  131.     inx = max(1,min(inx,j));
  132.  
  133.     if (numdlets)
  134.     {
  135.         j = 0;
  136.         while (j < inx)
  137.         {
  138.             if (DELETED(j))    inx++;
  139.             j++;
  140.         }
  141.         j--;
  142.     }
  143.     else
  144.         j = inx - 1;
  145.  
  146.     return (j);
  147. }
  148.  
  149. /* <dds_line>:
  150.  * Display the indicated file-index on the screen, at the indicated line
  151.  * (numbered from 0).
  152.  */
  153. void    dds_line(int inx)
  154. {
  155.     int    TOP_END,
  156.         line    = inx - top_line;
  157.     char    bfr    [CRT_COLS];
  158.  
  159.     if (line >= 0 && line < lpp1)
  160.     {
  161.         dirent_conv (bfr, FK_(inx));
  162.         if (dircmd_select(-2) == inx)
  163.         {
  164.             int    col = dirent_ccol()-1;
  165.  
  166.             bfr[col-1] = '*';    /* both marker        */
  167.             crt_high (bfr, col);    /* and highlighting    */
  168.         }
  169.         crt_text (bfr, line, 0);
  170.     }
  171. }
  172.  
  173. /* <dds_scroll>:
  174.  */
  175. int    dds_scroll (int ifile)
  176. {
  177.     return (crt_scroll(ifile, numfiles, dds_line));
  178. }
  179.  
  180. /* <dds_index>:
  181.  * Set the cursor to the indicated file-index, updating the path-summary
  182.  * as needed.  The current working-directory is always set to that of the
  183.  * current file.  Return the actual constrained value of 'ifile'.
  184.  */
  185. int    dds_index (int ifile)
  186. {
  187.     int    iline;
  188.  
  189.     if ((ifile = min(numfiles-1, max(0, ifile))) >= 0)
  190.     {
  191.         iline = dds_scroll(ifile);
  192.         dds_tell (nullC, ifile);
  193.         flist_chdir (PATHOF(ifile));
  194.         crt_move (iline, dirent_ccol());
  195.     }
  196.     return (ifile);
  197. }
  198.  
  199. /* <dds_all>:
  200.  * Display all lines in the current screen, beginning with the 'crt_top()'
  201.  * index.  Use "-1" argument for 'top_set' to permit screen refresh,
  202.  * and the -2 argument to do this while setting the top_line-marker to origin.
  203.  */
  204. void    dds_all(int top_set, int now_set)
  205. {
  206.     int    j, last, TOP_END;
  207.  
  208.     if (top_set >= 0)
  209.         crt_set (TRUE, top_set);
  210.     else
  211.     {
  212.         if (top_set == -2)    crt_set (TRUE, 0);
  213.         crt_clear();
  214.     }
  215.     top_line = crt_top();
  216.     end_line = crt_set (FALSE, min(numfiles-1, top_line + lpp2));
  217.  
  218.     last = end_line - top_line;
  219.     for (j = 0; j <= last; j++)
  220.         dds_line (j+top_line);
  221.  
  222.     /* Clear unused part of screen in case I have just shifted to
  223.        displaying a new directory. */
  224.     for (j = last+1; j < lpp1; j++)
  225.     {
  226.         if (*crtvec[j])
  227.         {
  228.             crt__EL (j+1, 1);
  229.             *crtvec[j] = '\0';
  230.         }
  231.     }
  232.     dds_index (now_set);
  233. }
  234.  
  235. /* <dds_tell>:
  236.  * The last line of the screen is used for two purposes:
  237.  *
  238.  *    Display the current file's pathname, and location in directory-list.
  239.  *    Display error messages.
  240.  *
  241.  * This routine is used to maintain both items.  The "set_beep" routine both
  242.  * sounds an audible alarm, as well as sets a flag "didbeep" which is
  243.  * cleared only after the user next types a character for command-input.
  244.  */
  245. void    dds_tell (char *msg_, int ifile)
  246. {
  247.     int    lpp    = crt_lpp(),
  248.         width    = crt_width()-1;    /* nominally 79    */
  249.     static
  250.     char    sFMT1[] = "%%-%d.%ds",
  251.         sFMT2[]    = "Path: %%-%d.%ds %%3d of %%3d ";
  252.     char    bfr[CRT_COLS+3],
  253.         format[sizeof(sFMT2)+6];
  254.  
  255.     if (!didbeep())
  256.     {
  257.         if (msg_)
  258.         {
  259.             set_beep();
  260.             sprintf (format, sFMT1, width, width);
  261.             sprintf (bfr, format, msg_);
  262.         }
  263.         else
  264.         {
  265.             if (ifile >= 0)
  266.             {
  267.                 sprintf (format, sFMT2, width-19, width-19);
  268.                 sprintf (bfr, format,
  269.                     PATHOF(ifile),
  270.                     dds_inx1(ifile), numfiles-numdlets);
  271.             }
  272.             else
  273.             {
  274.                 sprintf (bfr, "Reading: %3d files", numfiles);
  275.             }
  276.         }
  277.         if (msg_ == 0 && ifile < 0)
  278.             dds_while(bfr);
  279.         else
  280.         {
  281.             crt_high (bfr, strlen(bfr));
  282.             crt_text (bfr, lpp1, 0);
  283.         }
  284.     }
  285. }
  286.  
  287. #define    DDS_WORKING    14    /* leaves nominal 66 columns */
  288.  
  289. static    int    dds_working;    /* true iff we've displayed "Working..." */
  290. static    char    fmt_while[]    = "%%-%d.%ds %%-10.10s";
  291.  
  292. /* <dds_while>:
  293.  * Borrow the end of the status-line to show a short (10-char max) message
  294.  * while executing a long process, such as spawn or sort.
  295.  */
  296. void    dds_while (char *msg_)
  297. {
  298.     static    int    toggle = -1;
  299.     int    lpp    = crt_lpp(),
  300.         width    = crt_width() - DDS_WORKING;
  301.     static
  302.     char    msgbfr[CRT_COLS];
  303.     char    format[sizeof(fmt_while)+6];
  304.     char    *message;
  305.  
  306.     static    time_t last;
  307.     time_t    temp = time((time_t *)0);
  308.  
  309.     dds_working = TRUE;
  310.     if (temp != last)
  311.     {
  312.         toggle = !toggle;
  313.         last = temp;
  314.     }
  315.     if (toggle == 0)
  316.         message = "...Working";
  317.     else
  318.         message = "Working...";
  319.  
  320.     sprintf (format, fmt_while, width, width);
  321.     sprintf (msgbfr, format, msg_ ? msg_ : crtvec[lpp1], message);
  322.     crt_high (msgbfr, strlen(msgbfr));
  323.     crt_text (msgbfr, lpp1, 0);
  324.     crt_move (lpp, 1);    /* Leave cursor in known position */
  325. }
  326.  
  327. /* <dds_done>:
  328.  * Repaint the message-line without the "Working..." text when a command is
  329.  * completed.
  330.  */
  331. void    dds_done (void)
  332. {
  333.     if (dds_working)
  334.     {
  335.         int    lpp    = crt_lpp(),
  336.             width    = crt_width() - DDS_WORKING;
  337.         char    msgbfr[CRT_COLS];
  338.         char    format[sizeof(fmt_while)+6];
  339.  
  340.         dds_working = FALSE;
  341.         clrbeep();
  342.  
  343.         sprintf (format, fmt_while, width, width);
  344.         sprintf (msgbfr, format, crtvec[lpp1], " ");
  345.         dds_tell(msgbfr, -1);
  346.     }
  347. }
  348.  
  349. static    unsigned completion = 0;
  350.  
  351. /* <dds_ast1>:
  352.  * This procedure is called on completion of a spawned subprocess.  It tests
  353.  * the completion status for abnormality, printing an error message if so.
  354.  */
  355. void    dds_ast1 (void)
  356. {
  357.     flist_sysmsg (completion);
  358. }
  359.  
  360. /* <dds_spawn>:
  361.  * Spawn an external process, refreshing the screen as needed.  Spawning
  362.  * a process in VMS takes some time, so we must put out a message to the
  363.  * user (so he doesn't think the program died).  Then, some things (such
  364.  * as EDIT) will totally alter the screen.  For this reason, a hook is
  365.  * provided to refresh the screen when this process wakes up again.
  366.  */
  367. void    dds_spawn (
  368.     char    *cli_,        /* null iff DCL (default)        */
  369.     int    ifile,
  370.     char    *cmd_,        /* command (if null, simple SPAWN)    */
  371.     char    *msg_,
  372.     int    nowait,        /* 1=nowait, 0=wait (til process done)    */
  373.     int    refresh)    /* 0=norefresh, 1=line, 2=screen    */
  374. {
  375.     static    $DESCRIPTOR(DSC_line,"");
  376.     static    $DESCRIPTOR(DSC_prompt,"% ");
  377.     static    $DESCRIPTOR(DSC_cli,"");
  378.     int    status,
  379.         lpp    = crt_lpp(),
  380.         flags    = (nowait ? 1 : 0);
  381.     FILENT    *z = FK_(ifile);
  382.     char    fullname[MAX_PATH];
  383.  
  384.     /*
  385.      * If a CLI is specified, verify that it is accessible.  The file-spec
  386.      * must be all in uppercase.  (Note: to run a VMS CLI, we must be
  387.      * installed.)
  388.      */
  389.     if (cli_)
  390.     {
  391.         sprintf (fullname, "SYS$SYSTEM:%s.EXE;", cli_);
  392.         strucpy (fullname, nullC);
  393.         DSC_cli.dsc$a_pointer = fullname;
  394.         DSC_cli.dsc$w_length  = strlen(DSC_cli.dsc$a_pointer);
  395.         if (canopen(fullname))
  396.         {
  397.             warn ("CLI-%s not found", cli_);
  398.             return;
  399.         }
  400.     }
  401.  
  402.     if (cmd_)
  403.     {
  404.         DSC_line.dsc$a_pointer = cmd_;
  405.         DSC_line.dsc$w_length  = strlen(cmd_);
  406.     }
  407.     dds_while (msg_);
  408.     if (refresh & 2)
  409.         crt_quit (!cmd_);    /* I'll be gone a while    */
  410.  
  411.     status = lib$spawn (
  412.         (cmd_ ? &DSC_line : 0),
  413.         0, 0, &flags,
  414.         0, 0, &completion, 0,
  415.         nowait ? dds_ast1  : 0, 0,
  416.         cli_ ? &DSC_prompt : 0,
  417.         cli_ ? &DSC_cli    : 0);
  418.  
  419.     if (status != SS$_NORMAL)
  420.         warn ("Spawn failed: %X", status);
  421.     else
  422.     {
  423.         dds_hold (FALSE);    /* Do conditional hold    */
  424.         if (refresh)
  425.         {
  426.             if (refresh & 1)
  427.                 dirent_chk2 (ifile);
  428.             if (refresh & 2)
  429.             {
  430.                 crt_reset ();
  431.                 dds_all (-1, ifile);
  432.             }
  433.         }
  434.     }
  435.     if (! nowait)
  436.         dds_ast1 ();    /* Use completion routine to show message */
  437.     dds_index (ifile);
  438. }
  439.  
  440. /* <dds_hold>:
  441.  * Prompt the user for a carriage-return before continuing.  This procedure is
  442.  * called from 'dds_spawn' to conditionally (if "/hold" in effect) hold, or
  443.  * from other commands (e.g., "difference") to perform an unconditional hold.
  444.  *
  445.  * The hold function is used only for commands which are expected to wholly
  446.  * alter the screen.  A hold-point may be needed to trap system messages for
  447.  * the user.
  448.  *
  449.  * patch: We may implement an intermediate level of "/HOLD" where we will
  450.  *    force it if the completion-status is in error.
  451.  */
  452. void    dds_hold (int hold)
  453. {
  454.     int    got,
  455.         ok    = $VMS_STATUS_SUCCESS(completion);
  456.  
  457.     if (hold || flist_hold())
  458.     {
  459.         crt_move (1,1);        /* Force it to known position    */
  460.         if (!ok)        /* Try not to obscure message!!    */
  461.         {
  462.             crt_move (crt_lpp(),1);
  463.             putraw ("\r\n\n");
  464.         }
  465.         else
  466.             dds_tell (" ",-1);/* Make it clear first        */
  467.         clrbeep();        /* ...permit the next text:    */
  468.         dds_tell ("Hit RETURN to continue",-1);
  469.         clrbeep();
  470.         for (;;)
  471.         {
  472.             if ((got = toascii(getpad())) == '\n')
  473.                 break;
  474.             else if (got == '\r')
  475.                 break;
  476.         }
  477.     }
  478. }
  479.  
  480. /* <dds_fast>:
  481.  * Compute & return index to top (-1), bottom (+1) or middle (0) of display,
  482.  * ignoring possibly deleted files.
  483.  */
  484. int    dds_fast(int opt)
  485. {
  486.     int    TOP_END;
  487.  
  488.     switch (opt)
  489.     {
  490.     case DDS_U_S:    return (max(0, top_line - lpp1));
  491.     case DDS_U_C:    return (top_line);
  492.     case DDS_D_C:    return (end_line);
  493.     case DDS_D_S:    return (min(numfiles-1, end_line+lpp1));
  494.     default:    return ((top_line+end_line)/2);
  495.     }
  496. }
  497.  
  498. /* <dds_move>:
  499.  * Do short-distance (one-line), or within screen cursor-movement, checking
  500.  * limits:
  501.  *    DDS_U_S    = top of prior screen
  502.  *    DDS_U_C    = screen top
  503.  *    DDS_U_1    = up one line
  504.  *    DDS_0    = screen middle
  505.  *    DDS_D_1    = down one line
  506.  *    DDS_D_C    = screen bottom
  507.  *    DDS_D_S    = top of next screen
  508.  *
  509.  * The movement is complicated if there are deleted files within the current
  510.  * screen.  These are forbidden zones, since no operations may be performed
  511.  * on them.
  512.  *
  513.  * If the scrolling is out of the current screen, the appropriate scrolling
  514.  * action is first performed.  The returned value from this procedure can
  515.  * be used with the call
  516.  *    dds_line (ret);
  517.  * to properly reposition the cursor.  Out-of-screen scrolling assumes that
  518.  * the screen has already been packed.
  519.  */
  520. int    dds_move (int curfile, int opt)
  521. {
  522.     int    nxt, j, jm, jp, TOP_END;
  523.  
  524.     if (numfiles <= numdlets)
  525.         return (0);        /* Ensure a limit to recursion */
  526.  
  527.     switch (opt)
  528.     {
  529.     case DDS_U_S:
  530.         if (top_line)        /* Can I go upward ?        */
  531.             dds_scroll (top_line - lpp1);
  532.         return (dds_move(curfile, DDS_U_C));
  533.     case DDS_U_C:
  534.         for (nxt = top_line; nxt <= end_line; nxt++)
  535.             if (!DELETED(nxt))    return (nxt);
  536.         return (dds_move(curfile, DDS_U_1));
  537.     case DDS_U_1:
  538.         for (nxt = curfile-1; nxt >= 0; nxt--)
  539.             if (!DELETED(nxt))    return (nxt);
  540.         return (dds_move(curfile-1, DDS_D_1));
  541.     case  DDS_0:
  542.         nxt = dds_fast(DDS_0);
  543.         if (!DELETED(nxt))        return (nxt);
  544.         for (j = 0;
  545.             (jp = nxt+j) <= end_line && (jm = nxt-j) >= top_line;
  546.                 j++)
  547.         {
  548.             if (jp <= end_line && !DELETED(jp))    return (jp);
  549.             if (jm >= top_line && !DELETED(jm))    return (jm);
  550.         }
  551.         return (dds_move(nxt, DDS_D_1));
  552.     case  DDS_D_1:
  553.         for (nxt = curfile+1; nxt < numfiles; nxt++)
  554.             if (!DELETED(nxt))    return (nxt);
  555.         return (dds_move (curfile+1, DDS_U_1));
  556.     case  DDS_D_C:
  557.         for (nxt = end_line; nxt >= top_line; nxt--)
  558.             if (!DELETED(nxt))    return (nxt);
  559.         return (dds_move (curfile, DDS_D_1));
  560.     case DDS_D_S:
  561.         if (end_line < numfiles-1)
  562.         {
  563.             dds_scroll (dds_fast(DDS_D_S));    /* Do down-scroll */
  564.             return (dds_move (curfile, DDS_U_C));
  565.         }
  566.         else
  567.             return (dds_move (curfile, DDS_D_C));
  568.     }
  569. }
  570.  
  571. /* <dds_pack>:
  572.  * Scan the 'filelist[]' array for deleted files (usually outside the current
  573.  * screen).  When found, compress them out of the array, returning TRUE (the
  574.  * number of files compressed).  If no compression is done, return FALSE
  575.  * (no files).
  576.  */
  577. int    dds_pack (
  578.     int    *curfile_,    /* => current-file index, to adjust    */
  579.     int    inscreen)    /* != 0 if current screen can compress    */
  580.                 /* <= 0 if we adjust fixed-point    */
  581. {
  582.     int    j,
  583.         k,
  584.         oldtop    = crt_top(),
  585.         oldbot    = crt_end(),
  586.         select    = dircmd_select(-2),
  587.         adj    = 0,        /* amount to adjust indices    */
  588.         to_trim    = 0,        /* # of blank lines on end of list */
  589.         to_pack    = numdlets;    /* # of deleted files to scan    */
  590.  
  591.     if (!numdlets)
  592.         return (0);
  593.  
  594.     /*
  595.      * If any deleted files are at the end of the list, trim these first,
  596.      * since they involve no copying.  Note that this search will not go
  597.      * back before the current screen.
  598.      */
  599.     for (j = numfiles-1; j >= oldtop; j--)
  600.     {
  601.         if (DELETED(j))
  602.         {
  603.             numdlets--;
  604.             numfiles--;
  605.         }
  606.         else
  607.             break;
  608.     }
  609.     crt_set (FALSE, min(crt_end(), numfiles-1));
  610.     *curfile_ = min(*curfile_, numfiles-1);
  611.  
  612.     if (!(to_pack = numdlets))
  613.         return (0);
  614.  
  615.     /*
  616.      * If I have gotten this far, the deleted-file entries may be in the
  617.      * list anywhere, but not clustered at the end of the list.  For
  618.      * normal operation, only those entries which lie off the current
  619.      * screen may be collected.  (Operations such as the sorting require
  620.      * that all entries be compressed; sorting doesn't work on null entries.
  621.      */
  622.  
  623.     if (inscreen == 0)    /* subtract the number of files in screen */
  624.     {
  625.     int    TOP_END;
  626.         for (j = top_line; j <= end_line; j++)
  627.             if (DELETED(j))    to_pack--;
  628.     }
  629.  
  630.     if (to_pack <= 0)    return (FALSE);
  631.  
  632.     for (j = 0; j < numfiles; j++)
  633.     {
  634.         if (adj)
  635.         {
  636.             k = j - adj;
  637.             if (j == *curfile_)    *curfile_ = k;
  638.             if (j == crt_top())    crt_set (TRUE,  k);
  639.             if (j == crt_end())    crt_set (FALSE, k);
  640.         }
  641.         else
  642.             k = j;
  643.         if ((inscreen == 0) && (j >= oldtop) && (j <= oldbot))
  644.         {
  645.             if (adj)
  646.                 filelist[k] = filelist[j];
  647.         }
  648.         else
  649.         {
  650.             if (DELETED(j))
  651.             {
  652.                 adj++;
  653.                 if (j < select)    select--;
  654.             }
  655.             else if (adj)
  656.                 filelist[k] = filelist[j];
  657.         }
  658.     }
  659.  
  660.     numdlets -= adj;
  661.     numfiles -= adj;
  662.  
  663.     if (select >= 0 && (inscreen <= 0))    dircmd_select (select);
  664.  
  665.     return (adj);            /* Return # of files packed out */
  666. }
  667.  
  668. /* <dds_last>:
  669.  * Cleanup after command execution by moving to a non-deleted position, and
  670.  * packing entries which lie off the resulting screen.  Return TRUE if no
  671.  * more files remain, necessitating a QUIT.  The current-file index is updated
  672.  * to point to the proper file entry.
  673.  */
  674. int    dds_last (int *curfile_)
  675. {
  676.     if (multi_quit > 0)
  677.     {
  678.         multi_quit--;
  679.         return (TRUE);
  680.     }
  681.  
  682.     if (DELETED(*curfile_))
  683.         *curfile_ = dds_move (*curfile_, DDS_D_1);
  684.     if (numfiles > 0)
  685.     {
  686.         dds_pack (curfile_, FALSE);
  687.         dds_index (*curfile_);
  688.     }
  689.     return (numfiles <= 0);
  690. }
  691.  
  692. /* <dds_add>:
  693.  * Add a FILENT entry to 'filelist[]', updating the display as required.
  694.  * This code is used, for example, when an EDIT causes a new version to be
  695.  * displayed in a location, and the old version must be moved.  If there
  696.  * are any deleted-slots, put the entry there, otherwise put it at the end
  697.  * of the list.
  698.  */
  699. int    dds_add (    /* returns resulting index into 'filelist[]'    */
  700.     FILENT    *z)    /* => structure to insert/append to 'filelist[]' */
  701. {
  702.     int    j,
  703.         curfile = -1,
  704.         TOP_END;
  705.  
  706.     for (j = 0; j < numfiles; j++)    /* Find the first deleted-slot */
  707.     {
  708.         if (DELETED(j))
  709.         {
  710.             curfile = j;
  711.             numdlets--;    /* This slot is no longer deleted */
  712.             break;
  713.         }
  714.     }
  715.     if (curfile < 0)
  716.         curfile = dirent_add() - 1;    /* create a new entry    */
  717.  
  718.     filelist[curfile] = 0;
  719.     dirdata_add (z, &filelist[curfile]);
  720.  
  721.     if (curfile >= top_line && curfile <= (top_line + lpp2))
  722.         crt_set (FALSE, max(min(numfiles-1, top_line+lpp2), end_line));
  723.  
  724.     dds_width (z, curfile);            /* Latch column widths */
  725.     return (curfile);
  726. }
  727.  
  728. /* <dds_add2>:
  729.  * Add a new FILENT block at an empty slot.  Display it.  This code is used,
  730.  * for example, as a result of COPY.
  731.  */
  732. void    dds_add2 (FILENT *z, int curfile)
  733. {
  734.     dirdata_one (z, &filelist[curfile]);
  735.     dds_width (z, curfile);
  736. }
  737.  
  738. /* <dds_width>:
  739.  * Update the display-column widths.  If we have a new maxima, may refresh the
  740.  * entire display rather than simply the current line.
  741.  */
  742. void    dds_width (FILENT *z, int curfile)
  743. {
  744.     type_ccolumns    Ccolumns size_ccolumns;
  745.     static    char    Pcolumns [SIZEOF(ccolumns)+1] = "ntvpfxusa";
  746.     register j;
  747.  
  748.     memcpy (Ccolumns, ccolumns, sizeof(ccolumns));
  749.     if (dirent_width(z))
  750.     {
  751.         for (j = 0; j < SIZEOF(ccolumns); j++)
  752.         {
  753.             if (Ccolumns[j] != ccolumns[j])
  754.             {
  755.                 if ((j <= 1 && !pcolumns[j == 1])
  756.                 ||  (j == 2)
  757.                 ||  strchr(conv_list, Pcolumns[j]))
  758.                 {
  759.                     dds_all (crt_top(), curfile);
  760.                     return;
  761.                 }
  762.             }
  763.         }
  764.     }
  765.     dds_line (curfile);
  766. }
  767.