home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
vsiftp.vmssoftware.com
/
VSIPUBLIC@vsiftp.vmssoftware.com.tar
/
FREEWARE
/
FREEWARE40.ZIP
/
flistfrontend
/
src
/
fl.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-10-27
|
21KB
|
801 lines
#ifndef NO_IDENT
static char *Id = "$Id: fl.c,v 1.24 1995/10/27 21:30:44 tom Exp $";
#endif
/*
* Title: flist.c (FLIST main program)
* Author: T.E.Dickey
* Created: 31 Apr 1984
* Last update:
* 26 Oct 1995, err in stdarg conversion
* 22 Oct 1995, DEC-C clean-compile. Added seconds to "/dlong".
* 28 May 1995, use stdarg instead of VARARGS hack.
* 18 Mar 1995, prototypes
* 18 Feb 1995, port to AXP (DATENT mods, renamed 'alarm')
* 28 Feb 1989, log 132-wide messages instead of 80.
* 23 Feb 1989, use 'flist_chdir()'
* 04 Nov 1988, added expired-date
* 05 Oct 1985, added key-argument to 'flist_help'
* 17 Aug 1985, make 'flist_log' a write-thru from 'flist_lis'
* 14 Aug 1985, added code to make '?FIND' nested.
* 31 Jul 1985, added call on 'fledit_pack'. Restrict subset-options
* to exclude anything modifying XAB's in 'dirent'.
* 28 Jul 1985, 'getenv' does not appear to return a value when
* running from DEC/shell.
* 25 Jul 1985, added 'flist_lis' to open/write/close to listing
* file used for '?READ'.
* 24 Jul 1985, show invocation on /LOG. Make D_opt inherit properly
* when we do recursion.
* 23 Jul 1985, separated out options-code for use by EDIT command.
* 05 Jul 1985, modified calls on 'dirpath_init/free' to make a
* single structured character heap via 'nameheap'.
* 03 Jul 1985, cleanup 'filelist' definitions.
* 23 Jun 1985, if cannot enter level, set 'multi_quit'.
* 15 Jun 1985, use '&' in 'opts[]' declaration for CC2.0,
* typed 'dclarea', 'ropen2'.
* 14 Jun 1985, call it '/SINCE' instead of '/AFTER'. Provide
* alternate names /BACKUP, /CREATED, /MODIFIED
* for /DB, /DC, /DR.
* 13 Jun 1985, added '/AFTER', '/BEFORE' options
* 24 May 1985, corrected 'flset_mark', 'flset_hold' 'NO' tests
* 12 May 1985, corrected 'flset_date' switch
* 05 May 1985, chopped out the no-argument commands to leave them
* solely under control of 'dircmd' module. The
* 'flset_' stuff here is merely a remnant.
* 13 Apr 1985, set default-for /COMMAND, added '/LOG'.
* 08 Apr 1985, added /COMMAND option via 'getraw'
* 30 Mar 1985, added '?MEMORY' display command.
* 28 Mar 1985, provided conventional error-return point
* 21 Mar 1985, moved help-code into 'crt_help'. Also, added "-1"
* interpretation to 'D_mode' (/DS, /DL) to allow a
* very-short date format.
* 14 Mar 1985, modified help-file search to account for system
* installation.
* 03 Mar 1985, do flcols-init only once (hierarchical-inherit)
* 08 Feb 1985, check for limit to nesting-level
* 02 Feb 1985, use scrolling-margins
* 01 Feb 1985, broke 'dirread' from 'dirpath'.
* 14 Jan 1985, altered 'DCLOPT' init-data
* 10 Jan 1985, had wrong default for '/OWNER'
* 08 Jan 1985, renamed '/BACKUP' to '/DBACKUP', etc.
* 02 Jan 1985, use ^Z for quit-command.
* 30 Dec 1984, make clean call to 'flmore' for current-entry.
* 29 Dec 1984, added '/mark', '/nomark' to avoid keypad dependency.
* 24 Dec 1984, added 'D_mode' to drive day-of-week display.
* 22 Dec 1984, show allocation in "?SIZE"
* 21 Dec 1984, added 'flist_sysmsg'.
* 19 Dec 1984, moved 'cmdstk' init here, for hierarchy.
* 11 Dec 1984, added "?QUOTA". No "HOURS" option (implicit).
* 03 Dec 1984, use 'whoami'.
* 22 Oct 1984, added figure-of-merit (sysfom)
* 17 Oct 1984, do refresh in 'dircmd'
* 10 Sep 1984, got rid of <stdio>
* 03 Sep 1984, use "import"
* 30 Aug 1984, need at least 6 args in VARARGS
* 28 Aug 1984, cleanup buffer sizes, added '?'-prefix commands
* 15 Aug 1984, added date-display toggles (/dcreated, etc.)
* 14 Aug 1984, added 'warn2' entry
* 06 Aug 1984, added options BACKUP, REVISED and HOURS
* 27 Jul 1984, use 'dirpath_free'
* 25 Jul 1984, added 'cmd_arg' to 'dclarg()', added 'verify'
* 17 Jul 1984, fixed default on VERSIONS option
* 14 Jul 1984, use DCLOPT for option parsing
* 09 Jul 1984, fix kludgey 'more' interface.
* 30 Jun 1984, removed EDIT/VIEW function entirely to 'fledit'
* 26 Jun 1984
*
* Function: This program provides screen-oriented directory editing
* functions (rename, delete, change protection), combined
* with wildcard selection, and sorting on all visible
* attributes. The program design is influenced by the EDT
* editor, the DIRED program on Unix, and the FLIST program
* on IBM CMS.
*/
#define DIRENT
#include <stdlib.h>
#include <stdio.h>
#include <unixio.h>
#include <unixlib.h> /* for 'getcwd()' */
#include <signal.h> /* for 'sleep()' */
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <descrip.h>
#include <stsdef.h>
#include "cmdstk.h"
#include "getpad.h"
#include "dclarg.h" /* FIXME */
#include "flist.h"
#include "rmsio.h"
#include "whoami.h"
#include "dds.h"
#include "dirent.h"
#include "dircmd.h"
#include "dirpath.h"
#include "dirread.h"
#include "dclopt.h"
#include "getraw.h"
#include "freelist.h"
#include "nameheap.h"
#include "strutils.h"
#include "sysutils.h"
#undef EXIT_SUCCESS
#undef EXIT_FAILURE
#define EXIT_FAILURE (STS$M_INHIB_MSG | STS$K_ERROR)
#define EXIT_SUCCESS (STS$M_INHIB_MSG | STS$K_SUCCESS)
/*
* External data:
*/
import(filelist); import(numfiles);
import(AnyXAB);
import(A_opt); import(D_opt); import(M_opt);
import(O_opt); import(P_opt); import(V_opt);
import(D_mode);
import(dateflag); import(datechek);
import(filelink);
import(pathlist);
import(readlist);
import(readllen);
static void FlistTellVA (char *format, va_list ap);
static void FlistInfoVA (char *format, va_list ap);
/*
* Local (static) data:
*/
static
int beep_flag, warn_flag,
hold_flag, nesting_lvl, did_crt_init = FALSE;
static
char dftspec[] = "*.*;*";
/*
* Options:
* AFTER Append to display-list only files with dates *after*
* BEFORE Append to display-list only files with dates *before*
* COMMAND Read commands from a file until EOF, then terminal
* DBACKUP Show backup dates instead of creation dates
* DREVISED Show file revision dates instead of creation dates
* DEXPIRED Show file expriation dates instead of creation dates
* FAST Suppress all fields other than the filename (*3 faster)
* LOG Record commands and error messages
* NOALL Suppress filename if no privilege for other data
* NODATE Suppress creation-date+time
* NOOWNER Suppress display of owner code
* NOPROTECTION Suppress display of protection mask
* NOSIZE Suppress allocation (size) data
* NOVERSIONS Show only highest version of each file
*/
static
int _FALSE = FALSE,
_TRUE = TRUE,
Xfast, D_opt1, D_opt2, D_opt3, D_opt4;
#define COMMAND_ "command"
#define LOG_ "log"
static int H_opt;
static RFILE *LisRAB;
static RFILE *LogRAB;
static char *LogFile;
static char *CmdFile;
static char LogDft[] = "SYS$LOGIN:FLIST.LOG",
CmdDft[] = "SYS$LOGIN:FLIST.CMD",
LisFile[]= "SYS$LOGIN:FLIST.LIS";
#define SZ(n) &n,sizeof(n)
static
DCLOPT opts[] = {
{"fast", &_FALSE,0, SZ(Xfast), 1, 00017},
{COMMAND_, 0, CmdDft, 0,0, 1, 00200},
{LOG_, 0, LogDft, 0,0, 1, 00400},
{"date", &D_opt, 0, SZ(D_opt1), 1, 00001},
{"owner", &_TRUE, 0, SZ(O_opt), 1, 00002},
{"protection", &_TRUE, 0, SZ(M_opt), 1, 00004},
{"size", &_TRUE, 0, SZ(A_opt), 3, 00010},
#define SUBOPT 7
/* * * * * * end-of-subset-options * * * * * */
{"after", 0, 0, SZ(datechek), 2, 01000},
{"all", &_TRUE, 0, SZ(P_opt), 1, 00100},
{"backup", &_FALSE,0, SZ(D_opt2), 2, 00001},
{"before", 0, 0, SZ(datechek), 2, 01000},
{"created", &D_opt, 0, SZ(D_opt1), 1, 00001},
{"dbackup", &_FALSE,0, SZ(D_opt2), 2, 00001},
{"drevised", &_FALSE,0, SZ(D_opt3), 2, 00001},
{"dexpired", &_FALSE,0, SZ(D_opt4), 2, 00001},
{"help", &_FALSE,0, SZ(H_opt), 1, 07777},
{"modified", &_FALSE,0, SZ(D_opt3), 1, 00001},
{"since", 0, 0, SZ(datechek), 3, 01000},
{"versions", &_TRUE, 0, SZ(V_opt), 1, 00400}
};
int main (int argc, char **argv)
{
DCLARG *arg_ = argvdcl (argc, argv, dftspec, 0);
char *path_ = getenv("PATH");
CmdFile = dclarea (COMMAND_, MAX_PATH, (DCLOPT *)&opts, sizeof(opts));
LogFile = dclarea (LOG_, MAX_PATH, (DCLOPT *)&opts, sizeof(opts));
D_opt = TRUE; /* set default so we can inherit it */
if (dclchk (arg_, 0)) return(EXIT_FAILURE);
if (flist_opts (argc, argv, arg_, FALSE)) return(EXIT_FAILURE);
sysfom (nullC); /* Initialize timer */
nesting_lvl = 0;
hold_flag = FALSE;
crt_init (0);
did_crt_init = TRUE;
crt_margin (1, crt_lpp()-1);
flcols_init(); /* initialize set of columns to display */
dircmd_init(); /* Initialize command processor */
flist (arg_); /* Perform root-level call (wildcards) */
freelist (arg_); /* Release argument list */
if (path_)
flist_chdir (path_);/* restore original working directory */
flist_quit (EXIT_SUCCESS);
}
/*
* Set default directory.
*/
void flist_chdir(char *path)
{
static char old_path[MAX_PATH];
auto char new_path[MAX_PATH];
chdir(path);
getcwd(new_path, sizeof(new_path));
if (strcmp(old_path, new_path)) {
if (strcmp(new_path, path))
flist_log("! asked %s", path);
flist_log("! chdir %s", strcpy(old_path, new_path));
}
}
/*
* Common error/normal exit point:
*/
void flist_quit (int status)
{
if (did_crt_init)
crt_quit (TRUE); /* Make a clean exit */
exit (status);
}
void flist (DCLARG *dcl_)
{
int curfile = 0;
int Quit = FALSE;
CMDSTK *cmdstk_ = cmdstk_init();
flfind_init (nesting_lvl);
clrwarn();
filelist = 0;
if (Quit = (++nesting_lvl > 8))
warn ("Too many display levels");
else
{
dirpath_init (nesting_lvl);
dirent (dcl_); /* Read directory */
if (Quit = (numfiles <= 0))
warn ("No files found");
else
{
fledit_pack (); /* We got to a new level */
dds_all(0, curfile);
}
}
if (Quit) sleep (3); /* Make sure we see fatal message */
else dircmd(); /* Loop, executing all commands */
/*
* Release subdirectory storage and miscellaneous lists. 'filelist'
* is initialized only if we have successfully executed a directory
* read via 'dirent'.
*
* Note: 'dcl_' is free'd on return to 'dircmd' or main program.
*/
if (filelist)
{
cfree (filelist);
dirread_free ();
}
cmdstk_free(cmdstk_); /* ...and command history */
dirpath_free (nesting_lvl);
nameheap_set (--nesting_lvl);
}
/*
* Move the cursor to a new line. If no actual movement results, flag an
* error message so that the user will know what happened.
*/
void flist_move (int *curfile_, int ref, int code)
{
int old = *curfile_;
*curfile_ = dds_move (ref, code);
if ((ref == old) && (old == *curfile_) && (code /* 2's comp */ & 1))
warn ((code < 0) ? "Already at top" : "No more files");
}
/*
* Make the 'hold'-flag visible to external world (see: 'dds_hold()').
*/
int flist_hold(void)
{
return (hold_flag);
}
tDIRCMD(flset_hold)
{
hold_flag = (xcmd_[1] != 'n');
}
/*
* Set/clear selection-mark:
*/
tDIRCMD(flset_mark)
{
dircmd_select (xcmd_[1] == 'n' ? -1 : *curfile_);
}
/*
* Set flags controlling data-display:
*/
tDIRCMD(flset_date)
{
switch (xcmd_[2])
{
case 's': D_mode--; flist_date2 (*curfile_); break;
case 'l': D_mode++; flist_date2 (*curfile_); break;
case 'b': flist_date (*curfile_, 2); break;
case 'r': flist_date (*curfile_, 3); break;
case 'e': flist_date (*curfile_, 4); break;
default: flist_date (*curfile_, 1);
}
}
/*
* Set flag 'D_mode', which controls the width of the data-field in the display.
* Values:
* -1, very short, minimal 12-character format
* 0, nominal (date to minutes)
* 1, long (day of week plus date to minutes)
* 2, very long (day of week plus date to seconds)
*/
void flist_date2 (int curfile)
{
if (D_opt)
{
D_mode = max(-1, min(2, D_mode));
dds_all (dds_fast(DDS_U_C), curfile);
}
else
flist_date (curfile, 0);
}
/*
* Toggle the date-display mode. To make FLIST faster, normally only one
* type of filedate (CREATED,BACKUP,REVISED) is shown on the screen at a time.
*/
void flist_date (int curfile, int opt)
{
if (D_opt) /* Can I toggle it ? */
{
if (opt != D_opt)
{
D_opt = opt;
dds_all (dds_fast(DDS_U_C), curfile);
}
}
else
warn ("Date-toggle is invalid since NODATE was used");
}
/*
* FLIST sounds an audible alarm whenever a warning is sent to the status line.
* This, and other messages are normally cleared from the status line to show
* the current default directory, and the index into the file-list. We use
* flags (beep_flag, warn_flag) to avoid having the status line alternate too
* often between its default (pathname) and informational states:
*
* beep_flag - tested in 'dds_tell', prevents new data from being typed
* on the status line. This flag is normally reset on the first
* character of a new command, except when the character is the
* same as the last (single-character) command, which generated
* a message.
*
* warn_flag - (maintained here, tested in 'dircmd') is set by any
* warning message. If a single command generates multiple
* warnings, we do not really care to see more than one (e.g.,
* the case of a repeated, erroneous command). It is reset in
* 'dircmd' when we receive the first character of a new command
* (so that we can tell if we have the repeated single-character
* command).
*/
void set_beep(void) { beep_flag = TRUE; }
void clrbeep(void) { beep_flag = FALSE; }
int didbeep(void) { return (beep_flag); }
void clrwarn(void) { clrbeep(); warn_flag = FALSE; }
int didwarn(void) { return (warn_flag); }
/*
* Display a warning message at the bottom of the screen.
*/
static
void WarnVA (char *format, va_list ap)
{
if (!didbeep())
{
warn_flag = TRUE;
sound_alarm();
FlistTellVA (format, ap);
}
else if (++beep_flag < 99) /* Continue beeping, but limit it */
sound_alarm();
}
void warn (char *format, ...)
{
va_list ap;
va_start(ap, format);
WarnVA(format, ap);
va_end(ap);
}
/*
* This entry is used for the (rare) case in which a series of operations
* is requested, any of which may generate a warning message. If a warning
* has been posted, wait before setting a new message.
*/
void warn2 (char *format, ...)
{
va_list ap;
if (didbeep())
{
clrbeep();
sleep(1); /* patch: should use some type of hold */
}
va_start(ap, format);
WarnVA (format, ap);
va_end(ap);
}
/* <flist_tell>:
* Unconditionally send a warning message, overwriting any pending text:
*/
static
void FlistTellVA (char *format, va_list ap)
{
clrbeep();
FlistInfoVA (format, ap);
warn_flag = TRUE;
}
void flist_tell (char *format, ...)
{
va_list ap;
va_start(ap, format);
FlistTellVA (format, ap);
va_end(ap);
}
/* <flist_info>:
* Display informational message at the bottom of the screen. We expect no
* more than 2 filespecs, usually abbreviated. If a message is already
* pending (cf: 'didbeep'), then 'dds_tell' will not update the summary line.
*
* Route all error- and informational-messages through this point so that we
* can send them to a log-file.
*/
static
void FlistInfoVA (char *format, va_list ap)
{
char bfr[(3*MAX_PATH) + CRT_COLS];
vsprintf (bfr, format, ap);
if (did_crt_init)
dds_tell (bfr,-1);
else
{
putraw (bfr);
putraw ("\r\n");
}
flist_log ("%.132s", bfr);
}
void flist_info (char *format, ...)
{
va_list ap;
va_start(ap, format);
FlistInfoVA(format, ap);
va_end(ap);
}
/*
* Call 'flist_tell', when the argument is a VMS descriptor-string:
*/
int flist_tell_dsc (struct dsc$descriptor_s *ds_)
{
char bfr[CRT_COLS];
int len = ds_->dsc$w_length;
if (len > sizeof(bfr)) len = sizeof(bfr);
strncpy (bfr, ds_->dsc$a_pointer, len);
bfr[len] = EOS;
flist_tell ("%s", bfr);
return (EXIT_SUCCESS);
}
/*
* Display a system message at the bottom of the screen. We ignore success-
* and info-messages.
*
* Return TRUE on success so that the caller can use this routine to test
* completion as well. We assume that legal VMS status codes are nonzero
* unsigned integers.
*/
int flist_sysmsg (unsigned status)
{
char buffer[CRT_COLS];
if (!status || $VMS_STATUS_SUCCESS(status))
return (TRUE);
else
{
sysgetmsg (status, buffer, sizeof(buffer));
warn2 ("%s", buffer);
return (FALSE);
}
}
/*
* Display fatal-error message, exit:
*/
void error (int status, char *msg_)
{
auto char who[MAX_PATH];
if (did_crt_init)
{
crt_quit (TRUE); /* Make a clean exit */
did_crt_init = FALSE;
}
whoami (who, 3);
strcat (who, "-f-");
if (msg_) /* patch: We should send this to SYS$ERROR */
printf ("\n%s%s\n", who, msg_);
else
perror (who);
flist_quit (status ? status : EXIT_FAILURE);
}
/* <flist_help>:
* Provide help for this program. Route through this one point so we can
* use a common-name for the program.
*/
void flist_help (int curfile, char *key)
{
char bfr[CRT_COLS];
int len;
strcpy (bfr, "FLIST ");
if (key)
{
len = strlen (bfr);
strncat (&bfr[len], key, sizeof(bfr)-len-1);
bfr[sizeof(bfr)-1] = EOS;
}
crt_help (0, bfr);
}
/* <flist_log>:
* Write the specified information to the LOG-file, if it is open:
*/
static
void FlistLogVA (char *format, va_list ap)
{
if (LogRAB != 0)
{
register int j,k,next;
char bfr[CRT_COLS+(2*MAX_PATH)],
bfr2[CRT_COLS];
sprintf (bfr2, "%*s", nesting_lvl, " ");
vsprintf (bfr, format, ap);
/*
* Split the buffer if it contains embedded newlines. Ignore
* completely-null buffers.
*/
j = k = 0;
while (bfr[j])
{
next = nesting_lvl;
for (k = j; bfr[k] && bfr[k] != '\n'; k++)
bfr2[next++] = bfr[k];
rputr (LogRAB, bfr2, next);
j = bfr[k] ? k + 1: k;
}
}
}
void flist_log (char *format, ...)
{
va_list ap;
va_start(ap, format);
FlistLogVA (format, ap);
va_end(ap);
}
/*
* Make available the nesting-level:
*/
int flist_nest (void)
{
return (nesting_lvl);
}
/* <flist_opts>:
* Process options for either the main entry, or for EDIT-directory (subset).
* In the latter case, some options are unavailable because of a conflict of
* resources which would result. However, we generally inherit options from
* the main-level.
*
* Return TRUE if an error is found.
*
* patch: should resolve the ability to do COMMAND, LOG options within levels.
*/
int flist_opts (int argc, char **argv, DCLARG *arg_, int subset)
{
char msg[CRT_COLS + 8 * MAX_PATH];
if (subset) subset = SUBOPT; /* Number of entries to ignore */
if (dclopt (msg, arg_, &opts[subset],
sizeof(opts) - subset * sizeof(DCLOPT)))
{
if (subset) warn ("%s", msg);
else error (0, msg);
return (TRUE);
}
if (H_opt) {
int n;
printf("FLIST -- File List -- built %s\n", __DATE__);
printf("\nOptions:\n");
for (n = 0; n < sizeof(opts) / sizeof(opts[0]); n++)
{
printf("\t/%s\n", opts[n].opt_name);
}
exit(EXIT_SUCCESS);
}
if (D_opt1) D_opt = 1;
if (D_opt2) D_opt = 2;
if (D_opt3) D_opt = 3;
if (D_opt4) D_opt = 4;
if (! subset)
{
if (Xfast) A_opt = D_opt = M_opt = FALSE;
AnyXAB = (!Xfast || A_opt || D_opt || M_opt || O_opt);
D_mode = 0; /* Normally don't show day-of-week */
}
/*
* If a date-selection (/AFTER or /BEFORE) option was made, find the
* side-effect flags 'dateflag[]'.
*/
dateflag[0] = dateflag[1] = 0;
if (isOkDate(&datechek))
{
if (dateflag[1] = D_opt)
{
DCLARG *d_ = arg_;
while (d_ && ! dateflag[0])
{
strucpy (msg, d_->dcl_text);
if (!strncmp (msg, "/AF", 3)
|| !strncmp (msg, "/SI", 3))
dateflag[0] = 1;
else if (!strncmp (msg, "/BE", 3))
dateflag[0] = -1;
d_ = d_->dcl_next;
}
}
else
error (0, "Date-selection requires date-lookup");
}
if (! subset)
{
if (*LogFile)
{
if (!(LogRAB = ropen2 (LogFile, LogDft, "w")))
{
rerror();
return (TRUE);
}
else if (argc > 0)
{
int j;
size_t len = 0;
msg[0] = EOS;
for (j = 0; j < argc; j++)
{
char *parm = argv[j];
len += (strlen(parm) + 1);
if (len >= sizeof(msg) - 1)
break;
strcat (msg, " ");
strcat (msg, parm);
}
flist_log ("!%s", msg);
}
}
getraw_init(CmdFile,CmdDft);
/* Init this first, in case of error!! */
}
return (FALSE); /* no errors found */
}
/* <flist_lis>:
* Open/write-to/close listing-file used for miscellaneous displays which do
* not fit into either the status-line, or into the normal file-display list.
*
* If the operation is successful, return the actual-name for use by the caller
* (i.e., to browse it).
*/
char *flist_lis (char *format, ...)
{
char bfr[CRT_COLS+(2*MAX_PATH)];
if (format) /* If arguments given, open/write-to */
{
va_list ap;
if (! LisRAB)
{
if (!(LisRAB = ropen (LisFile, "w")))
{
rerror();
return (nullC);
}
}
va_start(ap, format);
FlistLogVA (format, ap);
vsprintf (bfr, format, ap);
va_end(ap);
rputr (LisRAB, bfr, strlen(bfr));
}
else if (LisRAB)
{
rclose (LisRAB);
LisRAB = 0;
}
return (LisFile);
}