bobm@rtech.UUCP (Bob Mcqueer) (12/21/87)
To find out what this beast does, read on into the archive - the "read.me" file is the first thing in it. Essentially, this is a hack that I figured somebody else might be able to use. {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 # strtok.c # korner.1 # korner.c # diagnostic.c # func # Makefile # This archive created: Sun Dec 20 16:22:31 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 a couple environments - Pyramid OSx (ucb univ.), more recently Sun, both 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 '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 '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 '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; char *index(), *rindex(); /* ** 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; /* ** 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 *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; { 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 '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 '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 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 # End of shell archive exit 0