v13i045: Convert (some) csh scripts to ksh scripts

Rich Salz rsalz at bbn.com
Wed Feb 17 11:28:16 AEST 1988


Submitted-by: Bob Mcqueer <rtech!bobm>
Posting-number: Volume 13, Issue 45
Archive-name: korner

[  As Bob admits, this is a hack.  Still and all, it might help folks make
   the great leap from csh to ksh.  I wouldn't know, and haven't tried
   this.  --r$ ]

This program feeds CSH scripts to a copy of /bin/csh then analyzes
the new aliases and variables that have been created, turning them
into (nearly) equivalent KSH shell functions.

{amdahl, sun, mtxinu, hoptoad, cpsc6a}!rtech!bobm
------ cut here -------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	read.me
#	Makefile
#	korner.1
#	diagnostic.c
#	korner.c
#	strtok.c
#	func
# This archive created: Sun Oct 25 11:39:24 1987
export PATH; PATH=/bin:$PATH
if test -f 'read.me'
then
	echo shar: will not over-write existing file "'read.me'"
else
cat << \SHAR_EOF > 'read.me'

The problem this stuff addresses:

	I find myself the lone ksh user in a csh community.  The tools
	I have to use on a daily basis are set up by a set of csh "source"
	files, which define some of those tools as csh aliases.  But,
	dadburn it, I want to use ksh!  AND I don't want to have to keep
	redoing my own setup files everytime somebody does something to
	the "standard" environment setup files which everybody is supposed
	to "source".  Take it as a given that I'm not going to convince
	anybody that the tool set should be defined in such a way that it
	is independent of which shell you run (the tools in question are
	often aliases for things to convert your environment on the fly -
	stuff like "eval `<gargle>`" where <gargle> is some script that
	generates sources and setenv's).

	What I need is to be able to "source" csh scripts under ksh, doing
	the same thing in my ksh environment that the script intended to
	do under csh.

What we got here:

	What I do is define a ksh function for "source" which:

	    1) uses csh to run the source file, dumping the resultant aliases,
		variables, directory stack, etc. off to a temp file.

	    2) runs a translator (the program "korner") over the temp file,
		producing a ksh file to run with "."  Korner is in no way
		shape or form intended to handle all the csh constructs
		which could be shoveled into aliases - but it does know
		how to convert simpler argument metasyntax.

	    3) does a "." of the file produced in 2), and removes the temp's.

	There are LOTS of holes here - the thing is basically a hack - but I've
	been using it and having it work.  Doing step 1) alleviates a LOT of
	the problem - let csh figure out its own silly script files and you
	only have to understand what finds its way into the types of aliases
	you need to translate.  A pitfall, of course, would be source files
	depending on environmental things set up by preceding source files
	which don't get passed from ksh into csh (cdpath setting, previous
	aliases, etc).   You would then have to make a source file of your
	own which sourced all the others, thus letting csh setup the whole
	mess all over again.

	This approach even seems to work fast enough to use it on the fly
	as I do - you could also save the output to periodically generate
	your own setup files.

	"func" contains my definition for the "source" function.  The variable
	KD is defined for use in temp file names.  If you define the variable
	KDOUT, the generated ksh "." files will get saved in there.  For my
	purposes, I also need to recognize "setenv" and "unsetenv" for the
	generated aliases.  You will note that it filters the printenv output
	which gets to the translator.  The "source" files I'm using this on do
	"setenv"'s of around 100 variables.  Not filtering out the ones that
	the csh "source" didn't change was causing ksh to run out of space
	for variable assignments by reassigning them all the time.

	korner.1 is the man page for "korner".  The "strtok.c" source
	file may be dispensed with if you have a system strtok() call
	available.

	To process the csh directory stack, it is expected that you have
	defined a "pushd" in your ksh definitions.  See korner.1.

caveats:

	I made no attempt to be comprehensive.  I just did a quick look over
	the csh man page, and translated the metasyntax that seemed reasonable
	to translate in processing the aliases.

	It's strictly a hand tooled parser - no formal grammar definition or
	anything like that.

	I've debugged this only to the extent that it works on the csh source
	files I've had to use.

	It's only ever run in one environment - Pyramid OSx, generally in the
	ucb universe, with what I believe is an early ksh version.  I haven't
	had occasion to try this thing on other machines / versions of shells,
	etc.  Some things, such as the expected output format of the various
	csh commands, were determined simply by examining what the local csh
	did, and slight differences in somebody else's csh might break it.

	Because of the above, it will likely have to be tweaked a bit to
	get YOUR set of csh setup files to run through it.

	Let me know of significant improvements, fixes, etc.

Bob McQueer
{amdahl, sun, mtxinu, hoptoad, cpsc6a}!rtech!bobm
SHAR_EOF
fi # end of overwriting check
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
OBJS = korner.o diagnostic.o strtok.o

CFLAGS = -O

korner: $(OBJS)
	cc -o korner $(OBJS)
SHAR_EOF
fi # end of overwriting check
if test -f 'korner.1'
then
	echo shar: will not over-write existing file "'korner.1'"
else
cat << \SHAR_EOF > 'korner.1'
.tr ~
.TH KORNER LOCAL 2/1/86
.UC
.SH NAME
korner - ksh environment converter
.SH SYNOPSIS
.B korner
[-dpushd] [-ssectchar] [-c] [-x] [-p] [-l] [-|infile] [outfile]
.SH DESCRIPTION
.I Korner
takes input describing environment settings in formats typically produced by
.I csh (1)
and
.I printenv (1)
and produces ksh commands to reflect them.  The purpose is to allow ksh users
to use csh "source" files, obtaining a ksh environment duplicating that set up
for csh.
.sp
The -c option will cause the ksh CDPATH to be replaced rather than
prefixed.  It is normally prefixed because csh does not pick up the
ksh CDPATH value as its own setting, the way it does the PATH setting.
.sp
The -x option will cause
.I korner
NOT to export the variables it assigns from its
.I printenv
style input.  Export is the default because this is implied by use
of the csh "setenv" builtin, and if the input really comes from
.I printenv,
the variables have to be exported for the
.I printenv
process to see them anyway.
.sp
The -p option will allow csh "prompt" settings to affect the ksh PS1 variable.
Normally, these are thrown away since csh doesn't inherit the ksh prompt, and
you don't want PS1 turned into the default csh "%".
.sp
The
.I -d
option will cause
.I korner
to use an alternate "pushd" command when processing the directory stack.
The directory stack environment input is reproduced by generating a series
of "pushd"'s, or the specified command.
.sp
The -l option causes unrecognized csh "set"'s to be assigned as unexported
ksh variables.  They are normally ignored.
.sp
The -s option specifies some character other than the default "+" used
to separate sections of
.I korner
input.  This is useful if the environment being processed insists on
naming something "+".
.sp
If the input file is missing or given as "-", standard input is used.
.sp
If the output file is missing standard output is used.
.SH INPUT~FORMAT
Input to
.I korner
is in several sections, each beginning
with a line starting with a "+" ( or the alternate character specified
by the -s option).  Lines following the section delimiter are understood
to represent a certain type of environment information until the next "+"
line, or end of file.
.sp
The "+E" line signifies variable assignments in the format produced by the
.I printenv
command.  They will be converted into pairs of export and variable
assignment statements.  If -x is set, the exports will not be done.
.sp
The "+A" line signifies csh
aliases in the format produced by the "alias" listing.
A ksh alias or function with the same name will be produced for each.
If no quoting or arguments are required, an alias is produced, otherwise
a function which attempts to mimic the
csh alias using ksh argument syntax.
.sp
The "+D" line should actually only have one line following it, which is
a directory stack, ala the csh "dirs" command.  The first word is assumed
to be the current directory, and is thrown out if a "cwd" setting has been seen.
The rest are used in reverse order
to generate "pushd" commands for the ksh directory stack.
.sp
The "+S" signifies the results of a csh "set" command.  Selected items
from the list which have ksh analogs are processed into settings of the
appropriate ksh variable.  "cwd" is used to set the current directory via a "cd"
command.  Use of "prompt" is optional, based on the "-p" flag.  "cdpath" is
usually prefixed to an existing
ksh CDPATH because invoking csh under ksh doesn't get the csh cdpath set
up from the ksh one, as happens with "path" from PATH.  Using command line
option "-c" makes the "cdpath" setting replace any existing CDPATH.
Settings not recognized as something with a ksh analog are usually ignored, but
may be turned into unexported variable assignments with the "-l" option.
.sp
A "+E" section should precede a "+S" section so that the csh setting of
things like "home" can override an exported-in HOME variable.  A "+S"
section should precede a "+D" section so that the HOME setting, if
changed, will be correct for interpretation of tilde's.
.sp
The "cd" command generated by
.I korner
based on either the "cwd" setting or the start of the "+D" section
is deferred to the end of its output.
.SH DIAGNOSTICS
Messages printed on standard error for failures and illegal input.
.SH BUGS~AND~LIMITATIONS
There are many things which cannot be handled for a variety of reasons.
Attempts have been made to at least generate diagnostics for these cases.
.sp
It is possible to define variables with strange
names, like "&", and actually get them into the environment table for
.I printenv
to see.  These will be thrown out with an error message.  Note that shell
processed "variables" like "$#" don't turn up in
.I printenv.
.I Korner
wants variable names beginning with a underscores or an alphabetic, and
containing underscores and alphanumerics.
.sp
Variable assignments containing embedded newlines will be handled provided
there is not an "=" somewhere after the newline.  In this situation, it
is impossible to tell from the output of
.I printenv
whether you have a single variable containing an embedded newline, or
two variable assignments.
.I Korner
simply assumes the latter, rather than trying to
guess whether you REALLY have a variable named " ", or <newline><space><equals>
was embedded in the preceding definition.
.sp
The metasyntax used by csh for describing aliases doesn't match up
exactly with ksh function argument syntax, and involved uses of it won't work.
.I Korner
attempts to translate what it can into ksh arguments, and produces
diagnostics for constructions that it doesn't understand.
.sp
Embedding csh built-ins in alias definitions will not work unless ksh
has similar built-ins, or definitions are made for them.  "setenv"
and "unsetenv" are two good examples.
.I Korner
does not attempt to generate diagnostics concerning built-ins.  The intent
is that you will define ksh aliases / functions for the ones you need.
.sp
In particular, csh "flow-of-control" in aliases won't work, since
.I korner
does not know about "if", "else" "foreach", etc.
.sp
Embedded newlines inside alias definitions will cause problems.  Fortunately,
csh does not reflect what kind of white space appeared in the alias definition,
only producing a newline in the alias definition if there is a literal
newline to be inserted somewhere.
.sp
Even with these severe limitations, the author has found that
.I korner
suffices to allow him to use tools and environment setup provided
as csh "source" files under ksh.  Many csh "source" scripts only use
complex csh interpretation to set up an environment, not to define
aliases.  Simply allowing csh to do so, then "dumping" the result for
.I korner
to analyze gets around a lot of translation problems.
.SH AUTHOR
R. L. McQueer
SHAR_EOF
fi # end of overwriting check
if test -f 'diagnostic.c'
then
	echo shar: will not over-write existing file "'diagnostic.c'"
else
cat << \SHAR_EOF > 'diagnostic.c'
#include <stdio.h>

/*
** generic error message routines.  Diag_xxx, may be externally set by caller.
**
** possible portability problem - use of several "long" arguments to pass
** stack through to underlying printf() family routine.
*/

/*
**
**	Copyright (c) 1987, Robert L. McQueer
**		All Rights Reserved
**
** Permission granted for use, modification and redistribution of this
** software provided that no use is made for commercial gain without the
** written consent of the author, that all copyright notices remain intact,
** and that all changes are clearly documented.  No warranty of any kind
** concerning any use which may be made of this software is offered or implied.
**
*/

char *Diag_file = "";		/* filename for use in diagnostic message */
int Diag_line = 1;		/* diagnostic line number */
FILE *Diag_fp = stderr;		/* output stream for messages */
char *Diag_cmd = "?";		/* command name for fatal() / usage() */

static int (*Fatcall)() = NULL;

/*
** print nonfatal diagnostic with line number from an input file.  Format
** compatible with "context"
*/
diagnostic(s,a,b,c,d,e,f)
char *s;
long a,b,c,d,e,f;
{
	fprintf(Diag_fp,"%s line %d: ",Diag_file,Diag_line);
	fprintf(Diag_fp,s,a,b,c,d,e,f);
	fprintf(Diag_fp,"\n");
}

/*
** print fatal error message and exit.  May call user set cleanup routine first.
** argument list passed to fatal() will also be passed to cleanup routine.
*/
fatal (s,a,b,c,d,e,f)
char *s;
long a,b,c,d,e,f;
{
	fprintf (Diag_fp,"%s: ",Diag_cmd);
	fprintf (Diag_fp,s,a,b,c,d,e,f);
	fprintf (Diag_fp,"\n");
	if (Fatcall != NULL)
		(*Fatcall) (s,a,b,c,d,e,f);
	exit (1);
}

/*
** set cleanup routine for fatal() calls
*/
fat_set (fn)
int (*fn) ();
{
	Fatcall = fn;
}

/*
** print usage message and exit.
*/
usage (s,a,b,c,d,e,f)
char *s;
long a,b,c,d,e,f;
{
	fprintf (Diag_fp,"usage: %s ",Diag_cmd);
	fprintf (Diag_fp,s,a,b,c,d,e,f);
	fprintf (Diag_fp,"\n");
	exit (1);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'korner.c'
then
	echo shar: will not over-write existing file "'korner.c'"
else
cat << \SHAR_EOF > 'korner.c'
/*
**
**	Copyright (c) 1987, Robert L. McQueer
**		All Rights Reserved
**
** Permission granted for use, modification and redistribution of this
** software provided that no use is made for commercial gain without the
** written consent of the author, that all copyright notices remain intact,
** and that all changes are clearly documented.  No warranty of any kind
** concerning any use which may be made of this software is offered or implied.
**
*/

#include <stdio.h>
#include <ctype.h>
#include <sys/param.h>

char Pwd[MAXPATHLEN];
char *Pushd = "pushd";
char *Umsg = "[-dpushd] [-ssectionchar] [-c] [-x] [-p] [-l] [-|infile] [outfile]";
char Sect = '+';

int Cdreplace = 0;
int Doprompt = 0;
int Xvar = 1;
int Lvar = 0;

/*
** limits.  BIGBUFFER determines how much text for a single alias, shell
** variable, etc. can be handled.  DIRDEPTH determines the depth of csh pushd's
** which can be accomodated.
*/
#define BIGBUFFER (MAXPATHLEN < 1800 ? 1800 : MAXPATHLEN)
#define DIRDEPTH 120

/*
** characters significant inside double quotes, thus requiring backslashes
*/
#define DQSIG "\\$`\""

extern char *Diag_cmd;
extern char *Diag_file;
extern int Diag_line;

main(argc,argv)
int argc;
char **argv;
{
	FILE *fpin;
	FILE *fpout;
	char *rindex();

	/*
	** set command for messages
	*/
	if ((Diag_cmd = rindex(*argv,'/')) == NULL)
		Diag_cmd = *argv;
	else
		++Diag_cmd;
	++argv;

	/*
	** check options
	*/
	while (argc > 1 && **argv == '-' && *((*argv)+1) != '\0')
	{
		--argc;
		for (++(*argv); **argv != '\0'; ++(*argv))
		{
			switch(**argv)
			{
			case 'x':
				Xvar = 0;
				break;
			case 'c':
				Cdreplace = 1;
				break;
			case 'l':
				Lvar = 1;
				break;
			case 'p':
				Doprompt = 1;
				break;
			case 'd':
				++(*argv);
				if (**argv == '\0')
				{
					if ((--argc) < 1)
						usage(Umsg);
					++argv;
				}
				Pushd = *argv;
				*argv += strlen(*argv) - 1;
				break;
			case 's':
				++(*argv);
				if (**argv == '\0')
				{
					if ((--argc) < 1)
					usage(Umsg);
					++argv;
				}
				Sect = **argv;
				*argv += strlen(*argv) - 1;
				break;
			default:
				usage(Umsg);
			}
		}
		++argv;
	}

	/*
	** set input stream, point Diag_file to name for diagnostics
	*/
	if (argc > 1 && strcmp(*argv,"-") != 0)
	{
		if ((fpin = fopen(*argv,"r")) == NULL)
			fatal("Can't open %s",*argv);
		Diag_file = *argv;
	}
	else
	{
		fpin = stdin;
		Diag_file = "<STDIN>";
	}

	/*
	** set ouput stream.
	*/
	if (argc > 2)
	{
		++argv;
		if ((fpout = fopen(*argv,"w")) == NULL)
			fatal("Can't open %s",*argv);
	}
	else
		fpout = stdout;

	parse(fpin,fpout);
}

/*
** output string intended to be inside double quotes, thus requiring
** backslashes
*/
dq_out(f,str)
FILE *f;
char *str;
{
	char *sp;
	char sc;

	for (;;)
	{
		for (sp = str; *sp != '\0' && index(DQSIG,*sp) == NULL; ++sp)
			;
		if (*sp != '\0')
		{
			sc = *sp;
			*sp = '\0';
			fprintf(f,"%s\\%c",str,sc);
			str = sp+1;
			continue;
		}
		break;
	}
	fprintf(f,"%s",str);
}

/*
** generic variable assignment
*/
var_assign(f,name,setting)
FILE *f;
char *name;
char *setting;
{
	fprintf(f,"%s=\"",name);
	dq_out(f,setting);
	fprintf(f,"\"\n");
}

/*
** find the starting delimiter of a name assignment, or return NULL
** designed to fail for wierd names that won't make proper
** ksh names
*/
char *
namstart(str,delim)
char *str;
char delim;
{
	char *eq;

	if (*str != '_' && !isalpha(*str))
		return (NULL);

	for (eq=str+1; *eq != delim; ++eq)
	{
		if (*eq == '\0')
			return (NULL);
		if (*eq != '_' && !isalnum(*eq))
			return (NULL);
	}

	return (eq);
}

/*
** process input from various enviroment commands, writing out ksh
** commands to simulate that environment.
**
** Input consists of lines beginning with "+" and a code which signifies
** what all following lines up to the next leading "+" contain:
**
**	+E:
**		environment variables, printed as <var>=<string>, as from
**		the "printenv" command.  Turned into pairs of exports and
**		variable assignments.
**
**	+S:
**		csh "set" command results.  Specific variables which have
**		ksh analogs (term, path, cdpath, home, cwd) handled specially,
**		others turned into straight variable assignments.  The
**		expected format is <name><tab><def>
**
**	+D:
**		followed by a directory stack list, as from the csh "dirs"
**		command.  Turned into pushd commands.  The directory stack
**		is assumed to be separated by spaces, with pwd as the first
**		item, followed by the top of the stack.
**
**	+A:
**		csh style aliases.  Turned into ksh aliases and functions,
**		as much as possible.  The expected format is <name><tab><def>.
**
** Note that the "+S" input should precede the "+D" input for the latter to
** interpret ~ properly if "home" has been changed.  "+E" should also precede
** the "+S" section to allow csh set commands to override ksh variables
** imported into another environment.
*/
static
parse(fin,fout)
FILE *fin;
FILE *fout;
{
	char state;
	char bufr[BIGBUFFER+1];
	char *index();
	char *ptr;
	char *slash;
	char sc;
	int nolook;

	state = ' ';
	Diag_line = 0;
	Pwd[0] = '\0';

	/*
	** we keep a lookahead record when needed rather than seeking back
	** because we may be reading from stdin.
	*/
	nolook = 1;
	for (;;)
	{
		if (nolook)
		{
			if (fgets(bufr,BIGBUFFER,fin) == NULL)
				break;
			bufr[strlen(bufr)-1] = '\0';
			++Diag_line;
		}
		else
			nolook = 1;
		if (bufr[0] == Sect)
		{
			state = bufr[1];
			if (index("ESAD",state) == NULL)
				diagnostic("Unknown section type: %c%c",
								Sect, state);
			continue;
		}
		switch (state)
		{
		case 'E':
			if ((ptr = namstart(bufr,'=')) == NULL)
			{
				diagnostic("Bad variable definition");
				break;
			}

			*ptr = '\0';
			if (Xvar)
				fprintf(fout,"export %s\n%s=\"",bufr,bufr);
			else
				fprintf(fout,"%s=\"",bufr);
			++ptr;

			/*
			** Read ahead a record, and if it doesn't appear
			** to be a new variable assignment or a state change,
			** stick in backslash-newline and continue processing
			*/
			for (;;)
			{
				dq_out(fout,ptr);
				if (fgets(bufr,BIGBUFFER,fin) == NULL)
					break;
				bufr[strlen(bufr)-1] = '\0';
				++Diag_line;

				/*
				** we don't use namstart here because it's
				** better to have a odd variable name fail
				** than have it appended to a good assignment.
				** Of course, now we miss legitimate '='
				** characters following embedded newlines
				*/
				if (bufr[0] == Sect || index (bufr,'=') != NULL)
				{
					nolook = 0;
					break;
				}
				fprintf(fout,"\\\n");
				ptr = bufr;
			}
			fprintf(fout,"\"\n");
			break;
		case 'A':
			if ((ptr = namstart(bufr,'\t')) == NULL)
			{
				diagnostic("Bad alias name");
				break;
			}
			*ptr = '\0';
			++ptr;
			al_parse(bufr,ptr,fout);
			break;
		case 'S':
			if ((ptr = namstart(bufr,'\t')) == NULL)
			{
				diagnostic("Bad \"set\" name");
				break;
			}
			*ptr = '\0';
			++ptr;
			set_check(bufr,ptr,fout);
			break;
		case 'D':
			dir_list(bufr,fout);
			break;
		default:
			break;
		}
	}

	/*
	** If we had pwd settings, issue a cd as the last thing
	*/
	if (Pwd[0] != '\0')
		fprintf(fout,"cd %s\n",Pwd);
}

/*
** perform pushd's to get csh dir stack on ksh - must be pushed in reverse
** order.  Having a limit built in is hardly elegant, but in practical
** terms, its hard to imagine directory stacks being useful after getting
** past a few directories deep.  Plus, a rather large limit is cheap in terms
** storage.
*/
static
dir_list(s,f)
char *s;
FILE *f;
{
	char *ptr;
	char *strtok();
	char *dir[DIRDEPTH];
	int numdir;

	/*
	** first item is pwd.  If we haven't set this because of a prior
	** setting (like "cwd" in a +S section), do so.
	*/
	ptr = strtok(s," ");
	if (ptr != NULL && Pwd[0] == '\0')
		strcpy(Pwd,ptr);

	numdir = 0;
	for (ptr = strtok(NULL," "); ptr != NULL; ptr = strtok(NULL," "))
	{
		if (numdir >= DIRDEPTH)
		{
			diagnostic("directory stack too deep - truncating");
			break;
		}
		dir[numdir] = ptr;
		++numdir;
	}

	/*
	** push in reverse order to get onto ksh stack
	** We assume pushd handles ~ notation.  As long as csh set
	** command is processed before directory stack, changes of
	** HOME will also be taken care of.
	*/
	for (--numdir; numdir >= 0; --numdir)
		fprintf (f,"%s %s\n",Pushd,dir[numdir]);
}

/*
** handle a csh alias
*/
static
al_parse(name,cdef,f)
char *name;
char *cdef;
FILE *f;
{
	int wantfunc;
	char kdef[BIGBUFFER];

	if (! al_xln(cdef, kdef, &wantfunc))
	{
		/*
		** explicit diagnostic for first error will have also
		** been produced.
		*/
		diagnostic("ALIAS %s=\"%s\" can't be translated\n",name,cdef);
		return;
	}

	if (wantfunc)
		fprintf(f,"function %s\n{\n\t%s\n}\n",name,kdef);
	else
	{
		fprintf(f,"alias %s=\"",name);
		dq_out(f,kdef);
		fprintf(f,"\"\n");
	}
}

/*
** translate subset of csh alias syntax into ksh.  If arguments or quotes
** are required, returned flag is TRUE, indicating need to define a function
** rather than an alias.  Diagnostics produced before error returns.
*/
static
al_xln(in,out,flag)
char *in, *out;
int *flag;
{
	char *index;
	int iscan;

	if (*in == '(')
		++in;

	*flag = 0;

	for (; *in != '\0'; ++in)
	{
		switch(*in)
		{

		/*
		** take care of csh level of backslashing
		** for literal !'s, etc.
		*/
		case '\\':
			++in;
			if (*in == '\0')
			{
				diagnostic("Trailing backslash");
				return (0);
			}
			sprintf(out,"\\%c", *in);
			out += 2;
			break;

		/*
		** csh meta-syntax
		*/
		case '!':
			*flag = 1;
			if ((iscan = meta(in+1,out)) <= 0)
				return (0);
			in += iscan;
			out += strlen(out);
			break;

		/*
		** set flag for double or single quote escaping
		*/
		case '"':
		case '\'':
			*flag = 1;	/* fall through */

		default:
			*out = *in;
			++out;
			break;
		}
	}

	if (out[-1] == ')')
		--out;
	*out = '\0';
	return (1);
}

/*
** translates what csh meta-syntax can be translated.  Returns number
** of characters scanned from input, 0 for untranslatable metacharacters
** Diagnostics produced at point of discerning error.
*/
static
meta(in,buf)
char *in;
char *buf;
{
	int a1, a2;
	int len;

	switch (*in)
	{
	case '*':
		strcpy(buf,"$*");
		return (1);
	case '^':
		strcpy(buf,"$1");
		return (1);
	case ':':
		++in;
		len = 2;
		switch(*in)
		{
		case '*':
			strcpy(buf,"$*");
			return (2);
		case '^':
			a1 = 1;
			break;
		case '-':
			a1 = 0;
			--in;
			len = 1;
			break;
		default:

			/*
			** only handle numeric parameters 0 - 9.
			*/
			if (isdigit(*in))
			{
				if (isdigit(in[1]))
				{
					diagnostic("Parameter out of range");
					return(0);
				}
				a1 = *in - '0';
				break;
			}
			diagnostic("Non-numeric '%c' following ':'", *in);
			return (0);
		}
		break;
	default:
		diagnostic("Unknown metacharacter '%c'",*in);
		return (0);
	}

	/*
	** only the : case gets here - a1 is set to number, len to
	** parsed character count.  We now handle ranges.  Once again
	** arguments > $9 are returned as 0.
	*/
	++in;
	if (*in != '-')
	{
		sprintf(buf,"$%d",a1);
		return (len);
	}
	++in;
	if (! isdigit(*in))
	{
		diagnostic ("Bad parameter range, numeric expected after '-'");
		return (0);
	}
	if (isdigit(in[1]))
	{
		diagnostic("Parameter out of range");
		return(0);
	}
	a2 = *in - '0';
	len += 2;
	if (a2 < a1)
	{
		diagnostic("Bad parameter range");
		return (0);
	}
	for ( ; a1 <= a2; ++a1)
	{
		sprintf(buf,"$%d ",a1);
		buf += 3;
	}
	--buf;
	*buf = '\0';
	return (len);
}

/*
** Some of the sets have meanings assigned to other names in ksh.  We
** translate these, and simply make unexported assignments for the remainder.
** Some of these depend on proper interaction between ksh and the spawned csh,
** and are hard to deal with.  We usually cheat on CDPATH by attaching the
** csh definitions to the ksh ones, since the ksh CDPATH doesn't get passed
** to the csh environment.
*/
static
set_check(name,setting,f)
char *name;
char *setting;
FILE *f;
{
	char *colonize();

	/*
	** TERM setting
	*/
	if (strcmp(name,"term") == 0)
	{
		var_assign(f,"TERM",setting);
		return;
	}
	/*
	** pwd is tracked by csh cwd.  We set this as a global variable,
	** generate the cd as the last thing, so that it doesn't matter
	** whether or not this comes before pushd calls.
	*/
	if (strcmp(name,"cwd") == 0)
	{
		strcpy(Pwd,setting);
		return;
	}
	/*
	** home = HOME
	*/
	if (strcmp(name,"home") == 0)
	{
		var_assign(f,"HOME",setting);
		return;
	}
	/*
	**  if we're taking it, prompt = PS1
	*/
	if (Doprompt && strcmp(name,"prompt") == 0)
	{
		var_assign(f,"PS1",setting);
		return;
	}
	/*
	**  translate path syntax, place in PATH
	*/
	if (strcmp(name,"path") == 0)
	{
		var_assign(f,"PATH",colonize(setting));
		return;
	}
	/*
	** prepend translated csh cdpath setting to any existing ksh CDPATH
	** unless Cdreplace flag is set.
	*/
	if (strcmp(name,"cdpath") == 0)
	{
		setting = colonize(setting);
		if (Cdreplace)
			var_assign(f,"CDPATH",setting);
		else
		{
			fprintf(f,"if [ -n \"$CDPATH\" ]\nthen\n\tCDPATH=\"");
			dq_out(f,setting);
			fprintf(f,":$CDPATH\"\nelse\n\tCDPATH=\"");
			dq_out(f,setting);
			fprintf(f,"\"\nfi\n");
		}
		return;
	}
	/*
	** shell (lower case) is probably set to /usr/ucb/csh.  set it to
	** $SHELL (upper case).  Don't use var_assign because that will
	** set it to a literal "$SHELL".  This is a "local" variable, so
	** only do this if Lvar anyway.
	*/
	if (Lvar && strcmp(name,"shell") == 0)
	{
		fprintf(f,"shell=\"$SHELL\"\n");
		return;
	}

	/*
	** optional unexported variable
	*/
	if (Lvar)
		var_assign(f,name,setting);
}

/*
** Translate csh paths to ksh syntax.  Done over top of old string.
** A small wrinkle - this routine will result in all pwd's in paths
** being represented by '.' instead of an empty entry, because of csh
** using "." in describing paths.
*/
static char *
colonize(cp)
char *cp;
{
	char *ptr;

	if (*(ptr = cp) == '(')
	{
		++cp;
		++ptr;
	}

	/* spaces become colons, terminate on EOS or parentheses */
	for ( ; *cp != '\0' && *cp != ')'; ++cp)
		if (*cp == ' ')
			*cp = ':';

	/* terminate string & delete any trailing colons */
	for (*cp = '\0'; cp != ptr && *(--cp) == ':'; *cp = '\0')
		;

	return (ptr);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'strtok.c'
then
	echo shar: will not over-write existing file "'strtok.c'"
else
cat << \SHAR_EOF > 'strtok.c'
#include <stdio.h>

static char *Save=NULL;

char *
strtok(str,delim)
char *str, *delim;
{
	char *tokstart, *tokend, *first_ch (), *last_ch();

	if (str != NULL)
		Save = str;

	if (Save == NULL)
		return (NULL);

	tokstart = first_ch (Save, delim);
	tokend = last_ch (tokstart, delim);
	Save = first_ch (tokend, delim);
	*tokend = '\0';

	if (*tokstart == '\0')
		return (NULL);

	return (tokstart);
}

static char *
first_ch (str,delim)
char *str,*delim;
{
	char *index ();
	char *f;

	for (f = str; *f != '\0' && index(delim,*f) != NULL; ++f)
		;

	return (f);
}

static char *
last_ch (str,delim)
char *str,*delim;
{
	char *index ();
	char *f;

	for (f = str; *f != '\0' && index(delim,*f) == NULL; ++f)
		;

	return (f);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'func'
then
	echo shar: will not over-write existing file "'func'"
else
cat << \SHAR_EOF > 'func'
function setenv
{
	export $1
	eval $1=\"$2\"
}

function unsetenv
{
	eval $1=\"\"
}

KD=/tmp/kd$$

function source
{
	if [ -s $1 ]
	then
		printenv >$KD.pe
		csh -c "source $1; echo +E >$KD.csh; /usr/ucb/printenv | comm  -23 - $KD.pe >>$KD.csh; echo +A >>$KD.csh; alias >>$KD.csh; echo +S >>$KD.csh; set >>$KD.csh; echo +D >>$KD.csh; dirs >>$KD.csh"
		korner $KD.csh $KD.env
		. $KD.env
		if [ -n "$KDOUT" ]
		then
			cat $KD.env >>$KDOUT
		fi
		rm $KD.*
	else
		echo source: $1 not found
	fi
}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
For comp.sources.unix stuff, mail to sources at uunet.uu.net.



More information about the Comp.sources.unix mailing list