[net.sources] VISTARTUP for other editors

geoff@callan.UUCP (09/05/84)

: This is a shar archive.  Extract with sh, not csh.
: The rest of this file will extract:
:
:	 README Makefile edstart.1 edstart.c
:
echo extracting - README
sed 's/^-//' > README << '/*EOF'

Well, I really liked the idea of Dolf Starreveld's VISTARTUP program,
which sets up EXINIT based on your extension.  Unfortunately, I'm not a
vi user.  So I hacked Dolf's program up to allow arbitrary editors, as
well as adding a feature or two (one of which I haven't actually implemented
yet).

Since I'm not a vi user, the example at the end of the manual page is a bit
weak.  I sure would appreciate it if one of you kind folks out there could
send me a more realistic one.  In particular, I think it needs a separator
character in the first line of the .editrc file.

Also, note that this program looks for an .editrc file in the current
directory as well as the home one, but does not check ownership of same.
If this is too much of a security hole for you, you should compile with
the SECURE option.

	Geoff Kuenning
	...!ihnp4!wlbr!desint!geoff
/*EOF
echo extracting - Makefile
sed 's/^-//' > Makefile << '/*EOF'
#
#	@(#)Makefile	1.2	9/1/84 18:42:25
#
#	Define USG if you have strchr/strrchr instead of index/rindex.
#	Define SECURE if you don't want to look in the current directory
#	    for the .editrc file.
#
CFLAGS=	-v -DUSG -O
/*EOF
echo extracting - edstart.1
sed 's/^-//' > edstart.1 << '/*EOF'
.TH EDSTART 1
.UC 4
.SH NAME
edstart \- start an editor with special options
.SH SYNOPSIS
.B edstart
[
.B editor options
]
.br
.SH DESCRIPTION
.I Edstart
reads lines from the file ".editrc" in either the current directory
or the users HOME directory
and examines the file arguments given to this program.
After this the real editor is started, essentially leaving all
arguments to the editor intact.
Depending on the extensions of the file arguments, commands are executed
as specified in the .editrc file.
.PP
Fields on each line of the .editrc file
are separated by colons.  The colons may be escaped with
a backslash if necessary.  No other escaping is needed, except for backslashes
themselves.
Blank lines are allowed, as long as they contain no whitespace.  Comment
lines are marked by a '#' character in the first column.
.PP
The first line of the .editrc file has the following format:
.br
<editor>:<variable>:<separator>:<terminator>:<getopt string>
.br
The editor field gives the full pathname of the editor for exec's.
.PP
The variable field gives the name of the environment variable to be changed
for editor initialization.  This field must include the equals sign;
for example, "EXINIT=".
.PP
The separator string will be appended to any previous value of the environment
variable before commands associated with an extension are added.
.PP
The terminator string is appended to the environment
variable following the newly-appended commands.
If the terminator string is found at the end of the environment
variable, it will be removed before any commands are appended to it.
.PP
The options field gives a getopt(3) string defining arguments to the
editor in use.  As an extension for use with vi, if the option string
begins with the character "+", options beginning with "+" will also
be accepted.
.PP
All subsequent lines of the file have the following format:
.br
<extension>:<priority>:<switches>:<editor commands>
.br
Blanks are not allowed in the first field, which ends at the first
colon.
.PP
The extension field defines the extension for which the commands
in the third field wil be executed. This field does not include the period,
i.e. for C source files it will be a single letter "c". Every character up
to but not including the first colon is made part of the extension,
this being the reason for blanks being forbidden in this field.
A null extension applies to files which have no extension.
.PP
As a special case, if the last line of the file has an extension field
of "." (which is impossible as a file extension), this line will be taken
as a default line which applies to any file unmatched by other lines.
.PP
The switches field gives switches or arguments to be added to the
editor command line.  However, this field is not currently implemented.
.PP
When several files with different extensions are used, the
prefered settings are determined as follows. Each time a file is seen
with an extension specified in the .editrc file, the value defined in
the priority field will be added to a total, maintained per extension.
After all files have been examined, the command(s) on the lines for the
extension which now has the largest total count, are executed. If you
don't want these priorities, simply define all priority fields to be
zero. In that case the commands corresponding to the extension of the
first file argument will be used.
.PP
Once the commands to execute are known, the environment is
altered. If the editor-initialization
variable was already present, the specified
commands are concatenated to the value of the editor-initialization
variable, with a
colon before it. If the environment variable
was undefined, it is added to the
environment. When the new environment is ready, the program execs
itself with the editor specified in the .editrc file, which thus
inherits the new environment.
.PP
The result of the use of the editor-initialization variable
by this program is that when working in
progressively deeper nested shells, the value of the variable will
increase in length. It also means that no command as specified in the
list in the .editrc file may rely on any default setting of options of the
editor,
because these may already have been altered by previous invocations of
the editor.
.SH EXAMPLES
The following example shows how the program might be used with the 'vi'
editor:
.nf
-/bin/vi|EXINIT=|||+t:rw:
c|5||set magic showmatch
|0||set magic
.|0||set nomagic
.fi
This causes the
.I magic
and
.I showmatch
options of the
.I vi
editor to be selected for C-language source files, the
.I magic
option to be selected for files with no extension,
and the
.I nomagic
option to be selected for all other files.
If the editor is invoked with more than one file and one of them is a C
source file, the C options will take priority.
.SH "SEE ALSO"
ex(1)
.SH BUGS
The switches field is unimplemented.
.SH AUTHOR
Geoff Kuenning.
Based on the program "vistartup" by
Dolf Starreveld, University of Amsterdam
.\"	@(#)edstart.1	1.1	9/1/84 18:21:32
/*EOF
echo extracting - edstart.c
sed 's/^-//' > edstart.c << '/*EOF'
static char	Sccs_Id[] = "@(#)edstart.c	1.2	9/1/84 18:42:29";

-/*
 *			   EDSTARTUP
 *
 *	Author: 	Geoff Kuenning
 *			Hacked from the program VISTARTUP, by
 *			Dolf Starreveld, University of Amsterdam.
 *
 *
 *	This program is a startup program for any editor.
 *	It runs under Unix System V and I think it will run
 *	on most other versions of Unix.
 *	The original program (by Dolf Starreveld) was designed
 *	after reading the disussion on the net about the advantages
 *	and disadvantages of mode lines, as available in ex/vi 3.7.
 *	The security danger of mode lines is avoided by using this
 *	program, keeping only the good things of mode lines.
 *	The hacked version was created to support other editors
 *	than vi.
 *
 *	The programs scans a special formatted file in the users home
 *	directory, ".editrc". In this file commands may be placed that
 *	are added to the command arguments and to the appropriate
 *	environment variable (EXINIT for vi) for the execution
 *	of this command only. For details see the manual page.
 *
 */

#include <stdio.h>

int		main ();	/* Set editor environment from args */
static void	filltab ();	/* Fill extension-action table */
static char *	getfld ();	/* Get a field from the table */
static int	extens ();	/* Extract an extension */
static void	fatal ();	/* Issue a fatal error */

#ifdef USG
#define index	strchr
#define rindex	strrchr
#endif

#define	MAXENV	256		/* Maximum number of environment variables */
#define INITLEN	512		/* Longest acceptable environment variable */
#define MAXEXTS 250		/* Maximum number of ext. lines in eatab */

#define SOURCE	"/.editrc"	/* Must contain a slash - see filltab */

#define option(c) ((c == '-') || (vioptions  &&  c == '+'))

static char *	editor = "/bin/vi"; /* Editor to be used */
static char *	envvar;		/* Environment variable to modify */
static char *	newenv[MAXENV];
static char **	newenvp = &newenv[0];
static char	newinit[INITLEN];
static char **	oldenv;
static char *	optstr;
static char *	prog = "edstartup";
static char *	separator;	/* Separator between command strings */
static int	tabentries = 0;
static char *	terminator;	/* Terminator for command strings */
static int	vioptions = 0;	/* Nonzero if vi options (+-type) in effect */

static struct ext_act		/* Commands to do for each extension */
    {
    char *	ext;		/* Extension */
    char *	act;		/* Required command */
    char *	switches;	/* Switches to add to command line */
    int		prior;		/* Per file priority value */
    int		tprior;		/* Total priority during after argument scan */
    }
		eatab[MAXEXTS];

extern char *	getenv ();
extern char *	index ();
extern char *	malloc ();
extern char *	rindex ();

int main (argc, argv, envp)
    int			argc;		/* Argument count */
    register char *	argv[];		/* Argument vector */
    register char *	envp[];		/* Environment vector */
    {
    char **		args;		/* Arguments to pass to editor */
    register int	extension;	/* Index into eatab */
    int			initdone = 0;	/* Nonzero if EXINIT (or such) seen */
    int			maxext = -1;	/* Index of high-priority extension */
    int			maxprior = -1;	/* Priority of high-pri ext */
    register char *	scratchp;	/* Handy scratch pointer */

    filltab ();

    args = argv;
    oldenv = envp;

    if (argc > 0)
	{
	prog = rindex (argv[0], '/');
	if (prog == NULL)
	    prog = argv[0];
	}

	/* Skip all options */
    while (argv++, --argc > 0  &&  option (argv[0][0]))
	{
	if (argv[0][0] == '+')			/* Kludge for good ol' vi */
	    continue;
	if (argv[0][1] == '-'			/* End of options? */
	  ||  argv[0][1] == '\0')		/* - is a file name (stdin) */
	    {
	    argc--;				/* Skip this one too */
	    argv++;
	    break;
	    }
	if ((scratchp = index (optstr, argv[0][1])) != NULL)
	    if (scratchp[1] == ':')		/* Does switch take an arg? */
		if (argv[0][2] == '\0')		/* If so, eat it */
		    {
		    argc--;
		    argv++;
		    }
	}

-/*
	If there are no file arguments, use the default entry.
*/
    if (argc <= 0				/* No file arguments? */
      &&  strcmp (eatab[tabentries - 1].ext, ".") == 0)
	maxext = tabentries - 1;

    while (argc--)				/* Process file arguments */
	{					/* Find extension index */
	extension = extens (*argv++);
	if (extension != -1)			/* Known ext, add to total */
	    {
	    eatab[extension].tprior += eatab[extension].prior;
	    if (eatab[extension].tprior > maxprior)
		{
		maxprior = eatab[extension].tprior;
		maxext   = extension;
		}
	    }
	}

	/*
	 * All filename arguments are processed. The extension with
	 * largest total priority is now known, so add its action
	 * as last part of the EXINIT string.
	 * If EXINIT was not defined, add it to the environment.
	 * If no files were given or no extension was recognized, do
	 * nothing.
	 */
	
    if (maxext != -1)
	{
-/*
    char *	switches;	/* Switches to add to command line
*/

	while (*envp)		/* Copy complete environment */
	    {
	    if (strncmp (envvar, *envp, strlen (envvar)) == 0)
		{
		strcpy (newinit, *envp);
		scratchp = &newinit[strlen (newinit)];
		scratchp -= strlen (terminator);
		if (strcmp (scratchp, terminator) == 0)
		    *scratchp = 0;	/* Get rid of any terminator */
		scratchp = &newinit[strlen (newinit)];
		scratchp -= strlen (separator);
		if (strcmp (scratchp, separator) == 0)
		    *scratchp = 0;	/* Get rid of any separator */
		strcat (newinit, separator);
		strcat (newinit, eatab[maxext].act);
		strcat (newinit, terminator);
		*envp = newinit;
		initdone = 1;
		}
	    *newenvp++ = *envp++;
	    }

	if (!initdone)
	    {
	    strcpy (newinit, envvar);
	    strcat (newinit, eatab[maxext].act);
	    strcat (newinit, terminator);
	    *newenvp++ = newinit;
	    }

	*newenvp = NULL;
	}

	/*
	 * The new environment list is now ready, exec the thing.
	 */
	
	/* Install program name */
    *args = rindex (editor, '/');
    if (*args == NULL)
	*args = editor;
    else
	(*args)++;
    execve (editor, args, (maxext == -1 ? oldenv : newenv));

    fatal ("Cannot exec %s.\n", args[0]);
    }

static void filltab ()
    {
    FILE *			fd;		/* File descriptor for table */
    char			linbuf[512];	/* Buffer for control lines */
    char *			line;		/* Pointer into linbuf */
    register int		nr;		/* Index into eatab */

    linbuf[0] = '\0';
    if ((line = getenv ("HOME")) != NULL)
	strcpy (linbuf, line);
    strcat (linbuf, SOURCE);

    nr = 0;

#ifdef SECURE
    if ((fd = fopen (linbuf, "r")) == NULL)
#else
-/*
	The following rindex is guaranteed to return a non-null value,
	because SOURCE contains a slash.
*/
    if ((fd = fopen (rindex (linbuf, '/') + 1, "r")) == NULL
      &&  (fd = fopen (linbuf, "r")) == NULL)
#endif
	fatal ("Couldn't open %s file\n", SOURCE);
    else
	{
-/*
	Header line:

<editor>|<variable>|<separator>|<terminator>|<getopt string>
*/
	while (fgets (linbuf, sizeof linbuf, fd) != NULL)
	    {
	    if (linbuf[0] == '#'  ||  linbuf[0] == '\n')
		continue;
	    line = linbuf;
	    editor = getfld (&line);
	    envvar = getfld (&line);
	    separator = getfld (&line);
	    terminator = getfld (&line);
	    optstr = getfld (&line);
	    if (*optstr == '+')		/* VI options mode? */
		{
		vioptions = 1;
		optstr++;
		}
	    break;
	    }
-/*
<extension>|<priority>|<switches>|<editor commands>
*/
	while (fgets (linbuf, sizeof linbuf, fd) != NULL)
	    {
	    if (linbuf[0] == '#'  ||  linbuf[0] == '\n')
		continue;
	    line = linbuf;
	    eatab[nr].ext = getfld (&line);	/* Extension */
	    eatab[nr].act = getfld (&line);	/* A fake! Actually priority */
	    eatab[nr].prior = atoi (eatab[nr].act);
	    free (eatab[nr].act);
	    eatab[nr].tprior = 0;
	    eatab[nr].switches = getfld(&line);	/* Switches to add to cmd */
	    eatab[nr].act = getfld(&line);	/* Cmds to add to envvar */
	    nr++;
	    }
	fclose (fd);
	}
    tabentries = nr;
    }

static char * getfld (buffer)
    char **		buffer;		/* Pointer to buffer pointer */
					/* ..ARGUMENT IS MODIFIED */
    {
    register char *	first;		/* First character of field */
    register char *	fldbuf;		/* Buffer for the field */
    register char *	fldcur;		/* Next character in fldbuf */
    register char *	last;		/* Last character + 1 of field */

    if ((first = *buffer) == NULL)
	fatal ("Internal error in getfld", 0);
    if (*first == '\0')
	fatal ("Illegal format of %s file\n", SOURCE);
    while (1)
	{
	if ((last = index (first, '|')) == NULL
	  &&  (last = index (first, '\n')) == NULL)
	    fatal ("Illegal format of %s file\n", SOURCE);
	if (last == first  ||  last[0] == '\n'
	  ||  last[-1] != '\\'  ||  (last - 1 > first  &&  last[-2] == '\\'))
	    break;
	/* We get here only if last points to the | of a \| sequence */
	first = last + 1;
	}
    first = *buffer;			/* Re-initialize first */
    if ((fldbuf = malloc (last - first + 1)) == NULL)
	fatal ("Out of memory space for tables\n", 0);
    for (fldcur = fldbuf;  first != last;  )
	{
	if (first[0] == '\\'  &&  (first[1] == '|'  ||  first[1] == '\\'))
	    first++;
	*fldcur++ = *first++;
	}
    *fldcur = '\0';
    *buffer = last + 1;			/* Return pointer past separator */
    return fldbuf;
    }

static int extens (path)
    register char *	path;
    {
    register int 	i;
    register int	j;
    register char *	ex;

    if ((ex = rindex (path, '.')) == NULL)
	ex = "";
    else
	ex++;

    for (j = 0; j < tabentries; j++)
	if (strcmp (eatab[j].ext, ex) == 0)
	    return (j);
-/*
	If the last entry in the table has an extension of "." (which is
	impossible to get at the end of the file), use it as the default
	value for all other files.
*/
    j--;
    if (strcmp (eatab[j].ext, ".") == 0)
	return (j);
    else
	return (-1);
    }

static void fatal (s1, s2, s3, s4)
    int *	s1;
    int *	s2;
    int *	s3;
    int *	s4;
    {
    fprintf (stderr, "%s: ", prog);
    fprintf (stderr, s1, s2, s3, s4);
    fprintf (stderr, "\n");
    exit (1);
    }
/*EOF
exit
-- 

	Geoff Kuenning
	Callan Data Systems
	...!ihnp4!wlbr!callan!geoff
	...!ihnp4!wlbr!desint!geoff