home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume13 / korner / korner.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-01-31  |  13.7 KB  |  738 lines

  1. /*
  2. **
  3. **    Copyright (c) 1987, Robert L. McQueer
  4. **        All Rights Reserved
  5. **
  6. ** Permission granted for use, modification and redistribution of this
  7. ** software provided that no use is made for commercial gain without the
  8. ** written consent of the author, that all copyright notices remain intact,
  9. ** and that all changes are clearly documented.  No warranty of any kind
  10. ** concerning any use which may be made of this software is offered or implied.
  11. **
  12. */
  13.  
  14. #include <stdio.h>
  15. #include <ctype.h>
  16. #include <sys/param.h>
  17.  
  18. char Pwd[MAXPATHLEN];
  19. char *Pushd = "pushd";
  20. char *Umsg = "[-dpushd] [-ssectionchar] [-c] [-x] [-p] [-l] [-|infile] [outfile]";
  21. char Sect = '+';
  22.  
  23. int Cdreplace = 0;
  24. int Doprompt = 0;
  25. int Xvar = 1;
  26. int Lvar = 0;
  27.  
  28. /*
  29. ** limits.  BIGBUFFER determines how much text for a single alias, shell
  30. ** variable, etc. can be handled.  DIRDEPTH determines the depth of csh pushd's
  31. ** which can be accomodated.
  32. */
  33. #define BIGBUFFER (MAXPATHLEN < 1800 ? 1800 : MAXPATHLEN)
  34. #define DIRDEPTH 120
  35.  
  36. /*
  37. ** characters significant inside double quotes, thus requiring backslashes
  38. */
  39. #define DQSIG "\\$`\""
  40.  
  41. extern char *Diag_cmd;
  42. extern char *Diag_file;
  43. extern int Diag_line;
  44.  
  45. main(argc,argv)
  46. int argc;
  47. char **argv;
  48. {
  49.     FILE *fpin;
  50.     FILE *fpout;
  51.     char *rindex();
  52.  
  53.     /*
  54.     ** set command for messages
  55.     */
  56.     if ((Diag_cmd = rindex(*argv,'/')) == NULL)
  57.         Diag_cmd = *argv;
  58.     else
  59.         ++Diag_cmd;
  60.     ++argv;
  61.  
  62.     /*
  63.     ** check options
  64.     */
  65.     while (argc > 1 && **argv == '-' && *((*argv)+1) != '\0')
  66.     {
  67.         --argc;
  68.         for (++(*argv); **argv != '\0'; ++(*argv))
  69.         {
  70.             switch(**argv)
  71.             {
  72.             case 'x':
  73.                 Xvar = 0;
  74.                 break;
  75.             case 'c':
  76.                 Cdreplace = 1;
  77.                 break;
  78.             case 'l':
  79.                 Lvar = 1;
  80.                 break;
  81.             case 'p':
  82.                 Doprompt = 1;
  83.                 break;
  84.             case 'd':
  85.                 ++(*argv);
  86.                 if (**argv == '\0')
  87.                 {
  88.                     if ((--argc) < 1)
  89.                         usage(Umsg);
  90.                     ++argv;
  91.                 }
  92.                 Pushd = *argv;
  93.                 *argv += strlen(*argv) - 1;
  94.                 break;
  95.             case 's':
  96.                 ++(*argv);
  97.                 if (**argv == '\0')
  98.                 {
  99.                     if ((--argc) < 1)
  100.                     usage(Umsg);
  101.                     ++argv;
  102.                 }
  103.                 Sect = **argv;
  104.                 *argv += strlen(*argv) - 1;
  105.                 break;
  106.             default:
  107.                 usage(Umsg);
  108.             }
  109.         }
  110.         ++argv;
  111.     }
  112.  
  113.     /*
  114.     ** set input stream, point Diag_file to name for diagnostics
  115.     */
  116.     if (argc > 1 && strcmp(*argv,"-") != 0)
  117.     {
  118.         if ((fpin = fopen(*argv,"r")) == NULL)
  119.             fatal("Can't open %s",*argv);
  120.         Diag_file = *argv;
  121.     }
  122.     else
  123.     {
  124.         fpin = stdin;
  125.         Diag_file = "<STDIN>";
  126.     }
  127.  
  128.     /*
  129.     ** set ouput stream.
  130.     */
  131.     if (argc > 2)
  132.     {
  133.         ++argv;
  134.         if ((fpout = fopen(*argv,"w")) == NULL)
  135.             fatal("Can't open %s",*argv);
  136.     }
  137.     else
  138.         fpout = stdout;
  139.  
  140.     parse(fpin,fpout);
  141. }
  142.  
  143. /*
  144. ** output string intended to be inside double quotes, thus requiring
  145. ** backslashes
  146. */
  147. dq_out(f,str)
  148. FILE *f;
  149. char *str;
  150. {
  151.     char *sp;
  152.     char sc;
  153.  
  154.     for (;;)
  155.     {
  156.         for (sp = str; *sp != '\0' && index(DQSIG,*sp) == NULL; ++sp)
  157.             ;
  158.         if (*sp != '\0')
  159.         {
  160.             sc = *sp;
  161.             *sp = '\0';
  162.             fprintf(f,"%s\\%c",str,sc);
  163.             str = sp+1;
  164.             continue;
  165.         }
  166.         break;
  167.     }
  168.     fprintf(f,"%s",str);
  169. }
  170.  
  171. /*
  172. ** generic variable assignment
  173. */
  174. var_assign(f,name,setting)
  175. FILE *f;
  176. char *name;
  177. char *setting;
  178. {
  179.     fprintf(f,"%s=\"",name);
  180.     dq_out(f,setting);
  181.     fprintf(f,"\"\n");
  182. }
  183.  
  184. /*
  185. ** find the starting delimiter of a name assignment, or return NULL
  186. ** designed to fail for wierd names that won't make proper
  187. ** ksh names
  188. */
  189. char *
  190. namstart(str,delim)
  191. char *str;
  192. char delim;
  193. {
  194.     char *eq;
  195.  
  196.     if (*str != '_' && !isalpha(*str))
  197.         return (NULL);
  198.  
  199.     for (eq=str+1; *eq != delim; ++eq)
  200.     {
  201.         if (*eq == '\0')
  202.             return (NULL);
  203.         if (*eq != '_' && !isalnum(*eq))
  204.             return (NULL);
  205.     }
  206.  
  207.     return (eq);
  208. }
  209.  
  210. /*
  211. ** process input from various enviroment commands, writing out ksh
  212. ** commands to simulate that environment.
  213. **
  214. ** Input consists of lines beginning with "+" and a code which signifies
  215. ** what all following lines up to the next leading "+" contain:
  216. **
  217. **    +E:
  218. **        environment variables, printed as <var>=<string>, as from
  219. **        the "printenv" command.  Turned into pairs of exports and
  220. **        variable assignments.
  221. **
  222. **    +S:
  223. **        csh "set" command results.  Specific variables which have
  224. **        ksh analogs (term, path, cdpath, home, cwd) handled specially,
  225. **        others turned into straight variable assignments.  The
  226. **        expected format is <name><tab><def>
  227. **
  228. **    +D:
  229. **        followed by a directory stack list, as from the csh "dirs"
  230. **        command.  Turned into pushd commands.  The directory stack
  231. **        is assumed to be separated by spaces, with pwd as the first
  232. **        item, followed by the top of the stack.
  233. **
  234. **    +A:
  235. **        csh style aliases.  Turned into ksh aliases and functions,
  236. **        as much as possible.  The expected format is <name><tab><def>.
  237. **
  238. ** Note that the "+S" input should precede the "+D" input for the latter to
  239. ** interpret ~ properly if "home" has been changed.  "+E" should also precede
  240. ** the "+S" section to allow csh set commands to override ksh variables
  241. ** imported into another environment.
  242. */
  243. static
  244. parse(fin,fout)
  245. FILE *fin;
  246. FILE *fout;
  247. {
  248.     char state;
  249.     char bufr[BIGBUFFER+1];
  250.     char *index();
  251.     char *ptr;
  252.     char *slash;
  253.     char sc;
  254.     int nolook;
  255.  
  256.     state = ' ';
  257.     Diag_line = 0;
  258.     Pwd[0] = '\0';
  259.  
  260.     /*
  261.     ** we keep a lookahead record when needed rather than seeking back
  262.     ** because we may be reading from stdin.
  263.     */
  264.     nolook = 1;
  265.     for (;;)
  266.     {
  267.         if (nolook)
  268.         {
  269.             if (fgets(bufr,BIGBUFFER,fin) == NULL)
  270.                 break;
  271.             bufr[strlen(bufr)-1] = '\0';
  272.             ++Diag_line;
  273.         }
  274.         else
  275.             nolook = 1;
  276.         if (bufr[0] == Sect)
  277.         {
  278.             state = bufr[1];
  279.             if (index("ESAD",state) == NULL)
  280.                 diagnostic("Unknown section type: %c%c",
  281.                                 Sect, state);
  282.             continue;
  283.         }
  284.         switch (state)
  285.         {
  286.         case 'E':
  287.             if ((ptr = namstart(bufr,'=')) == NULL)
  288.             {
  289.                 diagnostic("Bad variable definition");
  290.                 break;
  291.             }
  292.  
  293.             *ptr = '\0';
  294.             if (Xvar)
  295.                 fprintf(fout,"export %s\n%s=\"",bufr,bufr);
  296.             else
  297.                 fprintf(fout,"%s=\"",bufr);
  298.             ++ptr;
  299.  
  300.             /*
  301.             ** Read ahead a record, and if it doesn't appear
  302.             ** to be a new variable assignment or a state change,
  303.             ** stick in backslash-newline and continue processing
  304.             */
  305.             for (;;)
  306.             {
  307.                 dq_out(fout,ptr);
  308.                 if (fgets(bufr,BIGBUFFER,fin) == NULL)
  309.                     break;
  310.                 bufr[strlen(bufr)-1] = '\0';
  311.                 ++Diag_line;
  312.  
  313.                 /*
  314.                 ** we don't use namstart here because it's
  315.                 ** better to have a odd variable name fail
  316.                 ** than have it appended to a good assignment.
  317.                 ** Of course, now we miss legitimate '='
  318.                 ** characters following embedded newlines
  319.                 */
  320.                 if (bufr[0] == Sect || index (bufr,'=') != NULL)
  321.                 {
  322.                     nolook = 0;
  323.                     break;
  324.                 }
  325.                 fprintf(fout,"\\\n");
  326.                 ptr = bufr;
  327.             }
  328.             fprintf(fout,"\"\n");
  329.             break;
  330.         case 'A':
  331.             if ((ptr = namstart(bufr,'\t')) == NULL)
  332.             {
  333.                 diagnostic("Bad alias name");
  334.                 break;
  335.             }
  336.             *ptr = '\0';
  337.             ++ptr;
  338.             al_parse(bufr,ptr,fout);
  339.             break;
  340.         case 'S':
  341.             if ((ptr = namstart(bufr,'\t')) == NULL)
  342.             {
  343.                 diagnostic("Bad \"set\" name");
  344.                 break;
  345.             }
  346.             *ptr = '\0';
  347.             ++ptr;
  348.             set_check(bufr,ptr,fout);
  349.             break;
  350.         case 'D':
  351.             dir_list(bufr,fout);
  352.             break;
  353.         default:
  354.             break;
  355.         }
  356.     }
  357.  
  358.     /*
  359.     ** If we had pwd settings, issue a cd as the last thing
  360.     */
  361.     if (Pwd[0] != '\0')
  362.         fprintf(fout,"cd %s\n",Pwd);
  363. }
  364.  
  365. /*
  366. ** perform pushd's to get csh dir stack on ksh - must be pushed in reverse
  367. ** order.  Having a limit built in is hardly elegant, but in practical
  368. ** terms, its hard to imagine directory stacks being useful after getting
  369. ** past a few directories deep.  Plus, a rather large limit is cheap in terms
  370. ** storage.
  371. */
  372. static
  373. dir_list(s,f)
  374. char *s;
  375. FILE *f;
  376. {
  377.     char *ptr;
  378.     char *strtok();
  379.     char *dir[DIRDEPTH];
  380.     int numdir;
  381.  
  382.     /*
  383.     ** first item is pwd.  If we haven't set this because of a prior
  384.     ** setting (like "cwd" in a +S section), do so.
  385.     */
  386.     ptr = strtok(s," ");
  387.     if (ptr != NULL && Pwd[0] == '\0')
  388.         strcpy(Pwd,ptr);
  389.  
  390.     numdir = 0;
  391.     for (ptr = strtok(NULL," "); ptr != NULL; ptr = strtok(NULL," "))
  392.     {
  393.         if (numdir >= DIRDEPTH)
  394.         {
  395.             diagnostic("directory stack too deep - truncating");
  396.             break;
  397.         }
  398.         dir[numdir] = ptr;
  399.         ++numdir;
  400.     }
  401.  
  402.     /*
  403.     ** push in reverse order to get onto ksh stack
  404.     ** We assume pushd handles ~ notation.  As long as csh set
  405.     ** command is processed before directory stack, changes of
  406.     ** HOME will also be taken care of.
  407.     */
  408.     for (--numdir; numdir >= 0; --numdir)
  409.         fprintf (f,"%s %s\n",Pushd,dir[numdir]);
  410. }
  411.  
  412. /*
  413. ** handle a csh alias
  414. */
  415. static
  416. al_parse(name,cdef,f)
  417. char *name;
  418. char *cdef;
  419. FILE *f;
  420. {
  421.     int wantfunc;
  422.     char kdef[BIGBUFFER];
  423.  
  424.     if (! al_xln(cdef, kdef, &wantfunc))
  425.     {
  426.         /*
  427.         ** explicit diagnostic for first error will have also
  428.         ** been produced.
  429.         */
  430.         diagnostic("ALIAS %s=\"%s\" can't be translated\n",name,cdef);
  431.         return;
  432.     }
  433.  
  434.     if (wantfunc)
  435.         fprintf(f,"function %s\n{\n\t%s\n}\n",name,kdef);
  436.     else
  437.     {
  438.         fprintf(f,"alias %s=\"",name);
  439.         dq_out(f,kdef);
  440.         fprintf(f,"\"\n");
  441.     }
  442. }
  443.  
  444. /*
  445. ** translate subset of csh alias syntax into ksh.  If arguments or quotes
  446. ** are required, returned flag is TRUE, indicating need to define a function
  447. ** rather than an alias.  Diagnostics produced before error returns.
  448. */
  449. static
  450. al_xln(in,out,flag)
  451. char *in, *out;
  452. int *flag;
  453. {
  454.     char *index;
  455.     int iscan;
  456.  
  457.     if (*in == '(')
  458.         ++in;
  459.  
  460.     *flag = 0;
  461.  
  462.     for (; *in != '\0'; ++in)
  463.     {
  464.         switch(*in)
  465.         {
  466.  
  467.         /*
  468.         ** take care of csh level of backslashing
  469.         ** for literal !'s, etc.
  470.         */
  471.         case '\\':
  472.             ++in;
  473.             if (*in == '\0')
  474.             {
  475.                 diagnostic("Trailing backslash");
  476.                 return (0);
  477.             }
  478.             sprintf(out,"\\%c", *in);
  479.             out += 2;
  480.             break;
  481.  
  482.         /*
  483.         ** csh meta-syntax
  484.         */
  485.         case '!':
  486.             *flag = 1;
  487.             if ((iscan = meta(in+1,out)) <= 0)
  488.                 return (0);
  489.             in += iscan;
  490.             out += strlen(out);
  491.             break;
  492.  
  493.         /*
  494.         ** set flag for double or single quote escaping
  495.         */
  496.         case '"':
  497.         case '\'':
  498.             *flag = 1;    /* fall through */
  499.  
  500.         default:
  501.             *out = *in;
  502.             ++out;
  503.             break;
  504.         }
  505.     }
  506.  
  507.     if (out[-1] == ')')
  508.         --out;
  509.     *out = '\0';
  510.     return (1);
  511. }
  512.  
  513. /*
  514. ** translates what csh meta-syntax can be translated.  Returns number
  515. ** of characters scanned from input, 0 for untranslatable metacharacters
  516. ** Diagnostics produced at point of discerning error.
  517. */
  518. static
  519. meta(in,buf)
  520. char *in;
  521. char *buf;
  522. {
  523.     int a1, a2;
  524.     int len;
  525.  
  526.     switch (*in)
  527.     {
  528.     case '*':
  529.         strcpy(buf,"$*");
  530.         return (1);
  531.     case '^':
  532.         strcpy(buf,"$1");
  533.         return (1);
  534.     case ':':
  535.         ++in;
  536.         len = 2;
  537.         switch(*in)
  538.         {
  539.         case '*':
  540.             strcpy(buf,"$*");
  541.             return (2);
  542.         case '^':
  543.             a1 = 1;
  544.             break;
  545.         case '-':
  546.             a1 = 0;
  547.             --in;
  548.             len = 1;
  549.             break;
  550.         default:
  551.  
  552.             /*
  553.             ** only handle numeric parameters 0 - 9.
  554.             */
  555.             if (isdigit(*in))
  556.             {
  557.                 if (isdigit(in[1]))
  558.                 {
  559.                     diagnostic("Parameter out of range");
  560.                     return(0);
  561.                 }
  562.                 a1 = *in - '0';
  563.                 break;
  564.             }
  565.             diagnostic("Non-numeric '%c' following ':'", *in);
  566.             return (0);
  567.         }
  568.         break;
  569.     default:
  570.         diagnostic("Unknown metacharacter '%c'",*in);
  571.         return (0);
  572.     }
  573.  
  574.     /*
  575.     ** only the : case gets here - a1 is set to number, len to
  576.     ** parsed character count.  We now handle ranges.  Once again
  577.     ** arguments > $9 are returned as 0.
  578.     */
  579.     ++in;
  580.     if (*in != '-')
  581.     {
  582.         sprintf(buf,"$%d",a1);
  583.         return (len);
  584.     }
  585.     ++in;
  586.     if (! isdigit(*in))
  587.     {
  588.         diagnostic ("Bad parameter range, numeric expected after '-'");
  589.         return (0);
  590.     }
  591.     if (isdigit(in[1]))
  592.     {
  593.         diagnostic("Parameter out of range");
  594.         return(0);
  595.     }
  596.     a2 = *in - '0';
  597.     len += 2;
  598.     if (a2 < a1)
  599.     {
  600.         diagnostic("Bad parameter range");
  601.         return (0);
  602.     }
  603.     for ( ; a1 <= a2; ++a1)
  604.     {
  605.         sprintf(buf,"$%d ",a1);
  606.         buf += 3;
  607.     }
  608.     --buf;
  609.     *buf = '\0';
  610.     return (len);
  611. }
  612.  
  613. /*
  614. ** Some of the sets have meanings assigned to other names in ksh.  We
  615. ** translate these, and simply make unexported assignments for the remainder.
  616. ** Some of these depend on proper interaction between ksh and the spawned csh,
  617. ** and are hard to deal with.  We usually cheat on CDPATH by attaching the
  618. ** csh definitions to the ksh ones, since the ksh CDPATH doesn't get passed
  619. ** to the csh environment.
  620. */
  621. static
  622. set_check(name,setting,f)
  623. char *name;
  624. char *setting;
  625. FILE *f;
  626. {
  627.     char *colonize();
  628.  
  629.     /*
  630.     ** TERM setting
  631.     */
  632.     if (strcmp(name,"term") == 0)
  633.     {
  634.         var_assign(f,"TERM",setting);
  635.         return;
  636.     }
  637.     /*
  638.     ** pwd is tracked by csh cwd.  We set this as a global variable,
  639.     ** generate the cd as the last thing, so that it doesn't matter
  640.     ** whether or not this comes before pushd calls.
  641.     */
  642.     if (strcmp(name,"cwd") == 0)
  643.     {
  644.         strcpy(Pwd,setting);
  645.         return;
  646.     }
  647.     /*
  648.     ** home = HOME
  649.     */
  650.     if (strcmp(name,"home") == 0)
  651.     {
  652.         var_assign(f,"HOME",setting);
  653.         return;
  654.     }
  655.     /*
  656.     **  if we're taking it, prompt = PS1
  657.     */
  658.     if (Doprompt && strcmp(name,"prompt") == 0)
  659.     {
  660.         var_assign(f,"PS1",setting);
  661.         return;
  662.     }
  663.     /*
  664.     **  translate path syntax, place in PATH
  665.     */
  666.     if (strcmp(name,"path") == 0)
  667.     {
  668.         var_assign(f,"PATH",colonize(setting));
  669.         return;
  670.     }
  671.     /*
  672.     ** prepend translated csh cdpath setting to any existing ksh CDPATH
  673.     ** unless Cdreplace flag is set.
  674.     */
  675.     if (strcmp(name,"cdpath") == 0)
  676.     {
  677.         setting = colonize(setting);
  678.         if (Cdreplace)
  679.             var_assign(f,"CDPATH",setting);
  680.         else
  681.         {
  682.             fprintf(f,"if [ -n \"$CDPATH\" ]\nthen\n\tCDPATH=\"");
  683.             dq_out(f,setting);
  684.             fprintf(f,":$CDPATH\"\nelse\n\tCDPATH=\"");
  685.             dq_out(f,setting);
  686.             fprintf(f,"\"\nfi\n");
  687.         }
  688.         return;
  689.     }
  690.     /*
  691.     ** shell (lower case) is probably set to /usr/ucb/csh.  set it to
  692.     ** $SHELL (upper case).  Don't use var_assign because that will
  693.     ** set it to a literal "$SHELL".  This is a "local" variable, so
  694.     ** only do this if Lvar anyway.
  695.     */
  696.     if (Lvar && strcmp(name,"shell") == 0)
  697.     {
  698.         fprintf(f,"shell=\"$SHELL\"\n");
  699.         return;
  700.     }
  701.  
  702.     /*
  703.     ** optional unexported variable
  704.     */
  705.     if (Lvar)
  706.         var_assign(f,name,setting);
  707. }
  708.  
  709. /*
  710. ** Translate csh paths to ksh syntax.  Done over top of old string.
  711. ** A small wrinkle - this routine will result in all pwd's in paths
  712. ** being represented by '.' instead of an empty entry, because of csh
  713. ** using "." in describing paths.
  714. */
  715. static char *
  716. colonize(cp)
  717. char *cp;
  718. {
  719.     char *ptr;
  720.  
  721.     if (*(ptr = cp) == '(')
  722.     {
  723.         ++cp;
  724.         ++ptr;
  725.     }
  726.  
  727.     /* spaces become colons, terminate on EOS or parentheses */
  728.     for ( ; *cp != '\0' && *cp != ')'; ++cp)
  729.         if (*cp == ' ')
  730.             *cp = ':';
  731.  
  732.     /* terminate string & delete any trailing colons */
  733.     for (*cp = '\0'; cp != ptr && *(--cp) == ':'; *cp = '\0')
  734.         ;
  735.  
  736.     return (ptr);
  737. }
  738.