home *** CD-ROM | disk | FTP | other *** search
- #ifndef NO_IDENT
- static char *Id = "$Id: dds.c,v 1.17 1995/11/15 20:45:48 tom Exp $";
- #endif
-
- /*
- * Title: dds.c - "FLIST" display routines
- * Author: T.E.Dickey
- * Created: 03 May 1984
- * Last update:
- * 15 Nov 1995, size/allocated column-widths
- * 26 Oct 1995, mods to make 'dds_while()' animated
- * 18 Mar 1995, prototyped
- * 18 Feb 1995, renamed 'beep' to avoid conflict with curses.
- * 23 Feb 1989, use 'flist_chdir()'
- * 25 Aug 1985, added 'dds_width' entrypoint.
- * 24 Aug 1985, added 'dds_add2' entrypoint.
- * 20 Aug 1985, call 'dirent_width' in 'dds_add'
- * 30 Jul 1985, make 'dds_pack' adjust fixed-point if it lies off
- * screen.
- * 16 Jul 1985, use 'dirdata_add' in 'dds_add'.
- * 03 Jul 1985, cleanup of 'filelist' usage.
- * 11 Jun 1985, added cli-argument to dds_spawn
- * 22 Apr 1985, 'dds_cmd' code is now done in 'edtcmd'.
- * 02 Feb 1985, do 'crt_quit' before spawning to reset scroll-margin
- * 10 Jan 1985, added call to 'crt_reset' to fix up after spawning.
- * 29 Dec 1984, added 'dds_inx1', 'dds_inx2'. Highlight name if
- * line is selected.
- * 21 Dec 1984, use 'flist_sysmsg'
- * 10 Dec 1984, use 'dirent_chk2' instead of 'dirent_one'
- * 09 Sep 1984, use 'multi_quit' in 'dds_last'
- * 30 Aug 1984, added 'dds_last'
- * 29 Aug 1984, plugged hole in 'dds_index' (numfile==0)
- * 25 Aug 1984, cleanup buffer sizes
- * 14 Aug 1984, use 'dirent_ccol()'
- * 07 Aug 1984, cleanup of scrolling, declarations
- * 14 Jul 1984, added completion-routine to dds_spawn
- * 11 Jul 1984, added 'dds_hold' to spawn-process
- * 03 Jul 1984, changed 'conv_filent' to 'dirent_conv'
- * 19 Jun 1984
- * 29 May 1984, to chop into 'crt' and 'dds' modules
- *
- * Function: These routines perform screen display/refresh operations for
- * "FLIST".
- *
- * Entry:
- * dds_add: Add a new FILENT-block to 'filelist[]' (show it)
- * dds_add2: Add a new FILENT block at specific index
- * dds_all: Display all lines in the current screen
- * dds_ast1: Obtain completion status of spawned tasks
- * dds_fast: Returns index to top, middle or bottom of screen
- * dds_hold: Prompt for CR before continuing
- * dds_index: Set cursor to given 'filelist[]' entry
- * dds_inx1: Given 'filelist[]' index, return nth-file number
- * dds_inx2: Given nth-file number, return 'filelist[]' index
- * dds_last: Cleanup after command execution
- * dds_line: Display a given entry of 'filelist[]'
- * dds_move: Move to new index in 'filelist[]', may scroll
- * dds_pack: Pack deleted entries out of 'filelist[]'
- * dds_scroll: Do scrolling to given 'filelist[]' index
- * dds_spawn: Spawn an external process, refreshing screen
- * dds_tell: Update message/summary line (bottom)
- * dds_while: Show "Working..." messages
- * dds_width: Update display if (possible) column-width change
- */
-
- #include <stdlib.h>
- #include <stdio.h>
- #include <ctype.h>
- #include <types.h>
- #include <time.h>
- #include <string.h>
-
- #include <lib$routines.h>
- #include <rms.h>
- #include <ssdef.h>
- #include <stsdef.h>
- #include <descrip.h>
-
- #include "flist.h"
- #include "canopen.h"
- #include "dds.h"
- #include "dirent.h"
- #include "dirdata.h"
- #include "dircmd.h"
- #include "getpad.h"
-
- #include "strutils.h"
-
- import(filelist); import(numfiles); import(numdlets);
- import(ccolumns); import(pcolumns); import(conv_list);
-
- extern int multi_quit; /* >0 iff multi-level quit in progress */
-
- #define TOP_END top_line = crt_top(), end_line = crt_end(), lpp = crt_lpp()
- #define lpp2 (lpp-2)
- #define lpp1 (lpp-1)
-
- /* <dds_inx1>:
- * Given an index into 'filelist[]', return the number in the range
- * [1..(numfiles-numdlets)] which shows its real position in the display
- * list. This is used at the conclusion of each command to show the
- * current position in the display list.
- */
- int dds_inx1 (int ifile)
- {
- register
- int j,k;
-
- ifile++; /* put into range [1..numfiles] */
- ifile = max(1,min(ifile,numfiles));
- j = k = ifile;
-
- if (numdlets > 0)
- {
- while (--k >= 0)
- if (DELETED(k)) j--;
- }
- return (j);
- }
-
- /* <dds_inx2>:
- * Given a position-index in the display list, return the corresponding
- * index to 'filelist[]'. This is used in scrolling commands by index
- * number.
- */
- int dds_inx2 (int inx)
- {
- register
- int j = numfiles-numdlets;
-
- inx = max(1,min(inx,j));
-
- if (numdlets)
- {
- j = 0;
- while (j < inx)
- {
- if (DELETED(j)) inx++;
- j++;
- }
- j--;
- }
- else
- j = inx - 1;
-
- return (j);
- }
-
- /* <dds_line>:
- * Display the indicated file-index on the screen, at the indicated line
- * (numbered from 0).
- */
- void dds_line(int inx)
- {
- int TOP_END,
- line = inx - top_line;
- char bfr [CRT_COLS];
-
- if (line >= 0 && line < lpp1)
- {
- dirent_conv (bfr, FK_(inx));
- if (dircmd_select(-2) == inx)
- {
- int col = dirent_ccol()-1;
-
- bfr[col-1] = '*'; /* both marker */
- crt_high (bfr, col); /* and highlighting */
- }
- crt_text (bfr, line, 0);
- }
- }
-
- /* <dds_scroll>:
- */
- int dds_scroll (int ifile)
- {
- return (crt_scroll(ifile, numfiles, dds_line));
- }
-
- /* <dds_index>:
- * Set the cursor to the indicated file-index, updating the path-summary
- * as needed. The current working-directory is always set to that of the
- * current file. Return the actual constrained value of 'ifile'.
- */
- int dds_index (int ifile)
- {
- int iline;
-
- if ((ifile = min(numfiles-1, max(0, ifile))) >= 0)
- {
- iline = dds_scroll(ifile);
- dds_tell (nullC, ifile);
- flist_chdir (PATHOF(ifile));
- crt_move (iline, dirent_ccol());
- }
- return (ifile);
- }
-
- /* <dds_all>:
- * Display all lines in the current screen, beginning with the 'crt_top()'
- * index. Use "-1" argument for 'top_set' to permit screen refresh,
- * and the -2 argument to do this while setting the top_line-marker to origin.
- */
- void dds_all(int top_set, int now_set)
- {
- int j, last, TOP_END;
-
- if (top_set >= 0)
- crt_set (TRUE, top_set);
- else
- {
- if (top_set == -2) crt_set (TRUE, 0);
- crt_clear();
- }
- top_line = crt_top();
- end_line = crt_set (FALSE, min(numfiles-1, top_line + lpp2));
-
- last = end_line - top_line;
- for (j = 0; j <= last; j++)
- dds_line (j+top_line);
-
- /* Clear unused part of screen in case I have just shifted to
- displaying a new directory. */
- for (j = last+1; j < lpp1; j++)
- {
- if (*crtvec[j])
- {
- crt__EL (j+1, 1);
- *crtvec[j] = '\0';
- }
- }
- dds_index (now_set);
- }
-
- /* <dds_tell>:
- * The last line of the screen is used for two purposes:
- *
- * Display the current file's pathname, and location in directory-list.
- * Display error messages.
- *
- * This routine is used to maintain both items. The "set_beep" routine both
- * sounds an audible alarm, as well as sets a flag "didbeep" which is
- * cleared only after the user next types a character for command-input.
- */
- void dds_tell (char *msg_, int ifile)
- {
- int lpp = crt_lpp(),
- width = crt_width()-1; /* nominally 79 */
- static
- char sFMT1[] = "%%-%d.%ds",
- sFMT2[] = "Path: %%-%d.%ds %%3d of %%3d ";
- char bfr[CRT_COLS+3],
- format[sizeof(sFMT2)+6];
-
- if (!didbeep())
- {
- if (msg_)
- {
- set_beep();
- sprintf (format, sFMT1, width, width);
- sprintf (bfr, format, msg_);
- }
- else
- {
- if (ifile >= 0)
- {
- sprintf (format, sFMT2, width-19, width-19);
- sprintf (bfr, format,
- PATHOF(ifile),
- dds_inx1(ifile), numfiles-numdlets);
- }
- else
- {
- sprintf (bfr, "Reading: %3d files", numfiles);
- }
- }
- if (msg_ == 0 && ifile < 0)
- dds_while(bfr);
- else
- {
- crt_high (bfr, strlen(bfr));
- crt_text (bfr, lpp1, 0);
- }
- }
- }
-
- #define DDS_WORKING 14 /* leaves nominal 66 columns */
-
- static int dds_working; /* true iff we've displayed "Working..." */
- static char fmt_while[] = "%%-%d.%ds %%-10.10s";
-
- /* <dds_while>:
- * Borrow the end of the status-line to show a short (10-char max) message
- * while executing a long process, such as spawn or sort.
- */
- void dds_while (char *msg_)
- {
- static int toggle = -1;
- int lpp = crt_lpp(),
- width = crt_width() - DDS_WORKING;
- static
- char msgbfr[CRT_COLS];
- char format[sizeof(fmt_while)+6];
- char *message;
-
- static time_t last;
- time_t temp = time((time_t *)0);
-
- dds_working = TRUE;
- if (temp != last)
- {
- toggle = !toggle;
- last = temp;
- }
- if (toggle == 0)
- message = "...Working";
- else
- message = "Working...";
-
- sprintf (format, fmt_while, width, width);
- sprintf (msgbfr, format, msg_ ? msg_ : crtvec[lpp1], message);
- crt_high (msgbfr, strlen(msgbfr));
- crt_text (msgbfr, lpp1, 0);
- crt_move (lpp, 1); /* Leave cursor in known position */
- }
-
- /* <dds_done>:
- * Repaint the message-line without the "Working..." text when a command is
- * completed.
- */
- void dds_done (void)
- {
- if (dds_working)
- {
- int lpp = crt_lpp(),
- width = crt_width() - DDS_WORKING;
- char msgbfr[CRT_COLS];
- char format[sizeof(fmt_while)+6];
-
- dds_working = FALSE;
- clrbeep();
-
- sprintf (format, fmt_while, width, width);
- sprintf (msgbfr, format, crtvec[lpp1], " ");
- dds_tell(msgbfr, -1);
- }
- }
-
- static unsigned completion = 0;
-
- /* <dds_ast1>:
- * This procedure is called on completion of a spawned subprocess. It tests
- * the completion status for abnormality, printing an error message if so.
- */
- void dds_ast1 (void)
- {
- flist_sysmsg (completion);
- }
-
- /* <dds_spawn>:
- * Spawn an external process, refreshing the screen as needed. Spawning
- * a process in VMS takes some time, so we must put out a message to the
- * user (so he doesn't think the program died). Then, some things (such
- * as EDIT) will totally alter the screen. For this reason, a hook is
- * provided to refresh the screen when this process wakes up again.
- */
- void dds_spawn (
- char *cli_, /* null iff DCL (default) */
- int ifile,
- char *cmd_, /* command (if null, simple SPAWN) */
- char *msg_,
- int nowait, /* 1=nowait, 0=wait (til process done) */
- int refresh) /* 0=norefresh, 1=line, 2=screen */
- {
- static $DESCRIPTOR(DSC_line,"");
- static $DESCRIPTOR(DSC_prompt,"% ");
- static $DESCRIPTOR(DSC_cli,"");
- int status,
- lpp = crt_lpp(),
- flags = (nowait ? 1 : 0);
- FILENT *z = FK_(ifile);
- char fullname[MAX_PATH];
-
- /*
- * If a CLI is specified, verify that it is accessible. The file-spec
- * must be all in uppercase. (Note: to run a VMS CLI, we must be
- * installed.)
- */
- if (cli_)
- {
- sprintf (fullname, "SYS$SYSTEM:%s.EXE;", cli_);
- strucpy (fullname, nullC);
- DSC_cli.dsc$a_pointer = fullname;
- DSC_cli.dsc$w_length = strlen(DSC_cli.dsc$a_pointer);
- if (canopen(fullname))
- {
- warn ("CLI-%s not found", cli_);
- return;
- }
- }
-
- if (cmd_)
- {
- DSC_line.dsc$a_pointer = cmd_;
- DSC_line.dsc$w_length = strlen(cmd_);
- }
- dds_while (msg_);
- if (refresh & 2)
- crt_quit (!cmd_); /* I'll be gone a while */
-
- status = lib$spawn (
- (cmd_ ? &DSC_line : 0),
- 0, 0, &flags,
- 0, 0, &completion, 0,
- nowait ? dds_ast1 : 0, 0,
- cli_ ? &DSC_prompt : 0,
- cli_ ? &DSC_cli : 0);
-
- if (status != SS$_NORMAL)
- warn ("Spawn failed: %X", status);
- else
- {
- dds_hold (FALSE); /* Do conditional hold */
- if (refresh)
- {
- if (refresh & 1)
- dirent_chk2 (ifile);
- if (refresh & 2)
- {
- crt_reset ();
- dds_all (-1, ifile);
- }
- }
- }
- if (! nowait)
- dds_ast1 (); /* Use completion routine to show message */
- dds_index (ifile);
- }
-
- /* <dds_hold>:
- * Prompt the user for a carriage-return before continuing. This procedure is
- * called from 'dds_spawn' to conditionally (if "/hold" in effect) hold, or
- * from other commands (e.g., "difference") to perform an unconditional hold.
- *
- * The hold function is used only for commands which are expected to wholly
- * alter the screen. A hold-point may be needed to trap system messages for
- * the user.
- *
- * patch: We may implement an intermediate level of "/HOLD" where we will
- * force it if the completion-status is in error.
- */
- void dds_hold (int hold)
- {
- int got,
- ok = $VMS_STATUS_SUCCESS(completion);
-
- if (hold || flist_hold())
- {
- crt_move (1,1); /* Force it to known position */
- if (!ok) /* Try not to obscure message!! */
- {
- crt_move (crt_lpp(),1);
- putraw ("\r\n\n");
- }
- else
- dds_tell (" ",-1);/* Make it clear first */
- clrbeep(); /* ...permit the next text: */
- dds_tell ("Hit RETURN to continue",-1);
- clrbeep();
- for (;;)
- {
- if ((got = toascii(getpad())) == '\n')
- break;
- else if (got == '\r')
- break;
- }
- }
- }
-
- /* <dds_fast>:
- * Compute & return index to top (-1), bottom (+1) or middle (0) of display,
- * ignoring possibly deleted files.
- */
- int dds_fast(int opt)
- {
- int TOP_END;
-
- switch (opt)
- {
- case DDS_U_S: return (max(0, top_line - lpp1));
- case DDS_U_C: return (top_line);
- case DDS_D_C: return (end_line);
- case DDS_D_S: return (min(numfiles-1, end_line+lpp1));
- default: return ((top_line+end_line)/2);
- }
- }
-
- /* <dds_move>:
- * Do short-distance (one-line), or within screen cursor-movement, checking
- * limits:
- * DDS_U_S = top of prior screen
- * DDS_U_C = screen top
- * DDS_U_1 = up one line
- * DDS_0 = screen middle
- * DDS_D_1 = down one line
- * DDS_D_C = screen bottom
- * DDS_D_S = top of next screen
- *
- * The movement is complicated if there are deleted files within the current
- * screen. These are forbidden zones, since no operations may be performed
- * on them.
- *
- * If the scrolling is out of the current screen, the appropriate scrolling
- * action is first performed. The returned value from this procedure can
- * be used with the call
- * dds_line (ret);
- * to properly reposition the cursor. Out-of-screen scrolling assumes that
- * the screen has already been packed.
- */
- int dds_move (int curfile, int opt)
- {
- int nxt, j, jm, jp, TOP_END;
-
- if (numfiles <= numdlets)
- return (0); /* Ensure a limit to recursion */
-
- switch (opt)
- {
- case DDS_U_S:
- if (top_line) /* Can I go upward ? */
- dds_scroll (top_line - lpp1);
- return (dds_move(curfile, DDS_U_C));
- case DDS_U_C:
- for (nxt = top_line; nxt <= end_line; nxt++)
- if (!DELETED(nxt)) return (nxt);
- return (dds_move(curfile, DDS_U_1));
- case DDS_U_1:
- for (nxt = curfile-1; nxt >= 0; nxt--)
- if (!DELETED(nxt)) return (nxt);
- return (dds_move(curfile-1, DDS_D_1));
- case DDS_0:
- nxt = dds_fast(DDS_0);
- if (!DELETED(nxt)) return (nxt);
- for (j = 0;
- (jp = nxt+j) <= end_line && (jm = nxt-j) >= top_line;
- j++)
- {
- if (jp <= end_line && !DELETED(jp)) return (jp);
- if (jm >= top_line && !DELETED(jm)) return (jm);
- }
- return (dds_move(nxt, DDS_D_1));
- case DDS_D_1:
- for (nxt = curfile+1; nxt < numfiles; nxt++)
- if (!DELETED(nxt)) return (nxt);
- return (dds_move (curfile+1, DDS_U_1));
- case DDS_D_C:
- for (nxt = end_line; nxt >= top_line; nxt--)
- if (!DELETED(nxt)) return (nxt);
- return (dds_move (curfile, DDS_D_1));
- case DDS_D_S:
- if (end_line < numfiles-1)
- {
- dds_scroll (dds_fast(DDS_D_S)); /* Do down-scroll */
- return (dds_move (curfile, DDS_U_C));
- }
- else
- return (dds_move (curfile, DDS_D_C));
- }
- }
-
- /* <dds_pack>:
- * Scan the 'filelist[]' array for deleted files (usually outside the current
- * screen). When found, compress them out of the array, returning TRUE (the
- * number of files compressed). If no compression is done, return FALSE
- * (no files).
- */
- int dds_pack (
- int *curfile_, /* => current-file index, to adjust */
- int inscreen) /* != 0 if current screen can compress */
- /* <= 0 if we adjust fixed-point */
- {
- int j,
- k,
- oldtop = crt_top(),
- oldbot = crt_end(),
- select = dircmd_select(-2),
- adj = 0, /* amount to adjust indices */
- to_trim = 0, /* # of blank lines on end of list */
- to_pack = numdlets; /* # of deleted files to scan */
-
- if (!numdlets)
- return (0);
-
- /*
- * If any deleted files are at the end of the list, trim these first,
- * since they involve no copying. Note that this search will not go
- * back before the current screen.
- */
- for (j = numfiles-1; j >= oldtop; j--)
- {
- if (DELETED(j))
- {
- numdlets--;
- numfiles--;
- }
- else
- break;
- }
- crt_set (FALSE, min(crt_end(), numfiles-1));
- *curfile_ = min(*curfile_, numfiles-1);
-
- if (!(to_pack = numdlets))
- return (0);
-
- /*
- * If I have gotten this far, the deleted-file entries may be in the
- * list anywhere, but not clustered at the end of the list. For
- * normal operation, only those entries which lie off the current
- * screen may be collected. (Operations such as the sorting require
- * that all entries be compressed; sorting doesn't work on null entries.
- */
-
- if (inscreen == 0) /* subtract the number of files in screen */
- {
- int TOP_END;
- for (j = top_line; j <= end_line; j++)
- if (DELETED(j)) to_pack--;
- }
-
- if (to_pack <= 0) return (FALSE);
-
- for (j = 0; j < numfiles; j++)
- {
- if (adj)
- {
- k = j - adj;
- if (j == *curfile_) *curfile_ = k;
- if (j == crt_top()) crt_set (TRUE, k);
- if (j == crt_end()) crt_set (FALSE, k);
- }
- else
- k = j;
- if ((inscreen == 0) && (j >= oldtop) && (j <= oldbot))
- {
- if (adj)
- filelist[k] = filelist[j];
- }
- else
- {
- if (DELETED(j))
- {
- adj++;
- if (j < select) select--;
- }
- else if (adj)
- filelist[k] = filelist[j];
- }
- }
-
- numdlets -= adj;
- numfiles -= adj;
-
- if (select >= 0 && (inscreen <= 0)) dircmd_select (select);
-
- return (adj); /* Return # of files packed out */
- }
-
- /* <dds_last>:
- * Cleanup after command execution by moving to a non-deleted position, and
- * packing entries which lie off the resulting screen. Return TRUE if no
- * more files remain, necessitating a QUIT. The current-file index is updated
- * to point to the proper file entry.
- */
- int dds_last (int *curfile_)
- {
- if (multi_quit > 0)
- {
- multi_quit--;
- return (TRUE);
- }
-
- if (DELETED(*curfile_))
- *curfile_ = dds_move (*curfile_, DDS_D_1);
- if (numfiles > 0)
- {
- dds_pack (curfile_, FALSE);
- dds_index (*curfile_);
- }
- return (numfiles <= 0);
- }
-
- /* <dds_add>:
- * Add a FILENT entry to 'filelist[]', updating the display as required.
- * This code is used, for example, when an EDIT causes a new version to be
- * displayed in a location, and the old version must be moved. If there
- * are any deleted-slots, put the entry there, otherwise put it at the end
- * of the list.
- */
- int dds_add ( /* returns resulting index into 'filelist[]' */
- FILENT *z) /* => structure to insert/append to 'filelist[]' */
- {
- int j,
- curfile = -1,
- TOP_END;
-
- for (j = 0; j < numfiles; j++) /* Find the first deleted-slot */
- {
- if (DELETED(j))
- {
- curfile = j;
- numdlets--; /* This slot is no longer deleted */
- break;
- }
- }
- if (curfile < 0)
- curfile = dirent_add() - 1; /* create a new entry */
-
- filelist[curfile] = 0;
- dirdata_add (z, &filelist[curfile]);
-
- if (curfile >= top_line && curfile <= (top_line + lpp2))
- crt_set (FALSE, max(min(numfiles-1, top_line+lpp2), end_line));
-
- dds_width (z, curfile); /* Latch column widths */
- return (curfile);
- }
-
- /* <dds_add2>:
- * Add a new FILENT block at an empty slot. Display it. This code is used,
- * for example, as a result of COPY.
- */
- void dds_add2 (FILENT *z, int curfile)
- {
- dirdata_one (z, &filelist[curfile]);
- dds_width (z, curfile);
- }
-
- /* <dds_width>:
- * Update the display-column widths. If we have a new maxima, may refresh the
- * entire display rather than simply the current line.
- */
- void dds_width (FILENT *z, int curfile)
- {
- type_ccolumns Ccolumns size_ccolumns;
- static char Pcolumns [SIZEOF(ccolumns)+1] = "ntvpfxusa";
- register j;
-
- memcpy (Ccolumns, ccolumns, sizeof(ccolumns));
- if (dirent_width(z))
- {
- for (j = 0; j < SIZEOF(ccolumns); j++)
- {
- if (Ccolumns[j] != ccolumns[j])
- {
- if ((j <= 1 && !pcolumns[j == 1])
- || (j == 2)
- || strchr(conv_list, Pcolumns[j]))
- {
- dds_all (crt_top(), curfile);
- return;
- }
- }
- }
- }
- dds_line (curfile);
- }
-