home *** CD-ROM | disk | FTP | other *** search
- /*
- **
- ** 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);
- }
-