home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / editor / stevie / setenv.c < prev    next >
C/C++ Source or Header  |  1994-01-31  |  15KB  |  523 lines

  1. /*****************************************************************************
  2.  * A program for adding or changing environment variable values for MSDOS.
  3.  * The "set" command provided by command.com is very limited.  It fails to
  4.  * provide the ability to use quotation marks and escape characters and
  5.  * octal/hex constants in the value definition.  Setenv provides these
  6.  * abilities.
  7.  *
  8.  * Usage notes:
  9.  *
  10.  *    setenv <symbol> = <value>
  11.  *
  12.  *    <symbol> ::= legal MSDOS environment symbol.  Lower case converted
  13.  *             to uppercase.
  14.  *
  15.  *    <value>  ::= environment symbol value in one of three forms:
  16.  *
  17.  *             * No quotation marks.  The value is the literal string
  18.  *               of characters starting IMMEDIATELY after the equal
  19.  *               sign and extending to the end-of-line.
  20.  *
  21.  *             * Single quotation marks (').  The value is the literal
  22.  *               string enclosed in quotation marks.
  23.  *
  24.  *             * Double quotation marks (").  The value is the string
  25.  *               enclosed in double quotation marks.  Backslash escape
  26.  *               constructions are processed -- this includes the usual
  27.  *               C language constructions such as \n for newline and
  28.  *               \r for carriage return plus octal and hexadecimal
  29.  *               constants (\ddd & \0xdd, respectively).
  30.  *****************************************************************************/
  31.  
  32. /*****************************************************************************
  33.  * Based on a program by Alan J Myrvold (ajmyrvold@violet.waterloo.edu)
  34.  *
  35.  * WARNING WARNING WARNING - virtually no error checking is done !!
  36.  *                           use at own risk !!
  37.  *
  38.  * This program by Larry A. Shurr (las@cbema.ATT.COM)
  39.  *
  40.  * I added checking for env seg overrun, so now it's a little more robust.
  41.  *****************************************************************************/
  42.  
  43. /*****************************************************************************
  44.  *
  45.  * Notes by Alan J Myrgold:
  46.  *
  47.  * Technical information : A program's PSP contains a pointer at
  48.  * offset 44 (decimal) to a COPY of the parent's environment.
  49.  * The environment is a set of strings of the form NAME=value,
  50.  * each terminated by a NULL byte.
  51.  * An additional NULL byte marks the end of the environment.
  52.  * The environment area is ALWAYS paragraph aligned
  53.  * i.e. on a 16 byte boundary.
  54.  *
  55.  * Searching backwards from the PSP, I consistently find
  56.  * two copies of the envronment area.
  57.  *
  58.  * The program : finds the two areas
  59.  *               reads one into memory
  60.  *               udpates the specified environment variable
  61.  *               writes updated environment to parent environment
  62.  *****************************************************************************/
  63.  
  64. #include <stdio.h>
  65. #include <stdlib.h>
  66. #include <string.h>
  67. #include <time.h>
  68. #include <process.h>
  69. #include <conio.h>
  70. #include <dos.h>
  71.  
  72. #define FALSE 0
  73. #define TRUE  1
  74.  
  75. struct mcb {                /* MSDOS Memory Control Block */
  76.   unsigned char tag4D;            /* Tag field must = 0x4D */
  77.   unsigned int  next;            /* Segment base for next block */
  78.   unsigned int  size;            /* Memory block size in paragraphs */
  79. };
  80.  
  81.  
  82. unsigned env_size = 0;            /* Maintain size of environment */
  83. /***************************************************************************/
  84. int env_size_bytes(unsigned env_seg)
  85. /* Determine the length of the environment area in bytes */
  86. {
  87.     int n;
  88.  
  89.     n = 0;
  90.     while (peekb(env_seg,n) != 0) {
  91.           while (peekb(env_seg,n) != 0) n++;
  92.           n++;
  93.     }
  94.     return(n);
  95. }
  96. /***************************************************************************/
  97. int env_size_strings(unsigned env_seg)
  98. /* Determine how many strings are in the environment area */
  99. {
  100.     int k,n;
  101.  
  102.     k = n = 0;
  103.     while (peekb(env_seg,n) != 0) {
  104.           k++;
  105.           while (peekb(env_seg,n) != 0) n++;
  106.           n++;
  107.     }
  108.     return(k);
  109. }
  110. /***************************************************************************/
  111. int peek_cmp(unsigned seg1,unsigned seg2,int nbytes)
  112. /* A trivial compare routine for segement aligned data items */
  113. {
  114.    int i;
  115.  
  116.    for (i = 0; (i < nbytes) && (peekb(seg1,i) == peekb(seg2,i)); i++);
  117.    return(i == nbytes);
  118. }
  119. /***************************************************************************/
  120. void find_env(unsigned seg_ray[2])
  121. {
  122.     unsigned psp_seg,copy_of_seg,env_seg;
  123.     int k,n;
  124.  
  125. /* Find first copy of environment */
  126.     psp_seg = _psp;
  127.     copy_of_seg = peek(psp_seg,44);
  128.  
  129. /* Set return value to non-garabage */
  130.     seg_ray[0] = seg_ray[1] = copy_of_seg;
  131.  
  132. /* Search back to find 2 copies of environment */
  133.     env_size = n = env_size_bytes(copy_of_seg);
  134.     env_seg = copy_of_seg - 1;
  135.     for (k = 0; (env_seg != 0) && (k < 2); k++) {
  136.           while ((env_seg != 0) &&
  137.                  (peek_cmp(copy_of_seg,env_seg,n) == 0)) {
  138.                      env_seg--;
  139.           }
  140.           if (env_seg != 0) {
  141.               seg_ray[k] = env_seg;
  142.               env_seg--;
  143.           }
  144.     }
  145.  
  146. /* If not found, display error message and abort */
  147.     if (k != 2) {
  148.        fprintf(stderr,"Two copies of the environment were not found\n");
  149.        exit(-1);
  150.     }
  151. }
  152. /***************************************************************************/
  153. void read_env(unsigned env_seg,int *k,char ***s,char ***t)
  154. /* Read environment into a malloc'd array of malloc'd strings */
  155. {
  156.   int i,j,n;
  157.  
  158.   env_size = env_size_bytes(env_seg);
  159.  
  160.   *k = env_size_strings(env_seg);
  161.   *s = (char **) malloc((*k)*sizeof(char *));
  162.   *t = (char **) malloc((*k)*sizeof(char *));
  163.  
  164.   n = 0;
  165.   for (i = 0; i < *k; i++) {
  166.     for (j = 0; peekb(env_seg,n+j) != '='; j++);
  167.     (*s)[i] = (char *) malloc(j+1);
  168.     for (j = 0; peekb(env_seg,n+j) != '='; j++)
  169.       ((*s)[i])[j] = peekb(env_seg,n+j);
  170.     ((*s)[i])[j] = 0;
  171.     n += j + 1;
  172.     for (j = 0; peekb(env_seg,n+j) != 0; j++);
  173.     (*t)[i] = (char *) malloc(j+1);
  174.     for (j = 0; peekb(env_seg,n+j) != 0; j++)
  175.       ((*t)[i])[j] = peekb(env_seg,n+j);
  176.     ((*t)[i])[j] = 0;
  177.     n += j + 1;
  178.   }
  179. }
  180. /***************************************************************************/
  181. void write_env(unsigned env_seg, int k, char **s, char **t)
  182. /* Write the environment back out to memory */
  183. {
  184.   int i,j,n;
  185.  
  186.   struct mcb far *tmcb = (struct mcb far *)((long)(env_seg-1) << 16);
  187.  
  188.   if (tmcb->tag4D == 0x4D) {
  189.     unsigned env_seg_siz = tmcb->size << 4;
  190.     if (env_size < env_seg_siz) {
  191.       for (n = i = 0; i < k; i++) {
  192.         char *si = s[i];
  193.         char *ti = t[i];
  194.     for (j = 0; si[j] != 0; j++) pokeb(env_seg,n++,si[j]);
  195.     pokeb(env_seg,n++,'=');
  196.     for (j = 0; ti[j] != 0; j++) pokeb(env_seg,n++,ti[j]);
  197.     pokeb(env_seg,n++,0);
  198.       }
  199.       pokeb(env_seg,n,0);
  200.     } else {
  201.       fprintf(stderr,"Insufficient space in environment\n");
  202.       exit(-1);
  203.     }
  204.   } else {
  205.     fprintf(stderr,"Environment memory control block trashed\n");
  206.     exit(-1);
  207.   }
  208. }
  209. /***************************************************************************/
  210. char *get_env_var(int k,char **s,char **t,char *var)
  211. /* Return the value of the environment variable or NULL if not found */
  212. {
  213.     char *val;
  214.     int i;
  215.  
  216.     val = NULL;
  217.     for (i = 0; i < k; i++) if (stricmp(s[i],var) == 0) val = t[i];
  218.  
  219.     return(val);
  220. }
  221.  
  222. /***************************************************************************/
  223. void set_env_var(int *k,char ***s,char ***t,char *var,char *val)
  224. /* Set a new or existing environment variable to a new value */
  225. {
  226.   int i,done;
  227.  
  228.   done = 0;
  229.   for (i = 0; i < *k; i++) {
  230.     if (stricmp((*s)[i],var) == 0) {
  231.       /* Existing variable */
  232.       done = 1;
  233.       env_size -= strlen((*t)[i]);
  234.       free((*t)[i]);
  235.       (*t)[i] = (char *) malloc(1+strlen(val));
  236.       strcpy((*t)[i],val);
  237.       env_size += strlen((*t)[i]);
  238.     }
  239.   }
  240.  
  241.   if (!done) {
  242.     /* New environment variable */
  243.     (*k)++;
  244.     *s = realloc(*s,(*k)*sizeof(char *));
  245.     *t = realloc(*t,(*k)*sizeof(char *));
  246.     (*s)[*k-1] = (char *) malloc(1+strlen(var));
  247.     strcpy((*s)[*k-1],var);
  248.     strupr((*s)[*k-1]);
  249.     (*t)[*k-1] = (char *) malloc(1+strlen(val));
  250.     strcpy((*t)[*k-1],val);
  251.     /* Length of name  + length of '=' + length of value + length of '\0' */
  252.     env_size += (strlen((*s)[*k-1]) + 1 + strlen((*t)[*k-1]) + 1);
  253.   }
  254. }
  255. /***************************************************************************/
  256. void show_env(int k,char **s,char **t)
  257. /* Display the array of environment strings */
  258. {
  259.    int i;
  260.    for (i = 0; i < k; i++) printf("%s=%s\n",s[i],t[i]);
  261. }
  262. /***************************************************************************/
  263. void get_cmdline(char *cmd)
  264. /* Read raw command line text into string buffer */
  265. {
  266.     char far *pcmd;
  267.  
  268.     int idx,odx;
  269.  
  270.     pcmd = (char far *)((long)_psp << 16) + 128L;
  271.  
  272.     for (idx = *pcmd++, odx = 0; idx > 0; idx--, odx++) {
  273.       cmd[odx] = *pcmd++;
  274.     }
  275.  
  276.     cmd[odx] = '\0';
  277. }
  278. /***************************************************************************/
  279. char_in(char ch, char *set)
  280. /* Determine if a character is in a set of characters */
  281. {
  282.   do {
  283.     if (ch == *set) return(TRUE);
  284.   } while ((int)*(++set));
  285.   return(FALSE);
  286. }
  287. /***************************************************************************/
  288. char get_num(char *cmd, int *pidx)
  289. /* Interpret octal or hexadecimal constant in string */
  290. {
  291.   int   accum  = 0;
  292.   char  ch;
  293.   int   f_scan = TRUE;
  294.   int   idx    = *pidx;
  295.   int   limit;
  296.   char *nch    = cmd+idx;
  297.   char *och    = nch+1;
  298.   int   radix;
  299.  
  300. #define HEXDIG "0123456789ABCDEFabcdef"
  301.  
  302.   if (*nch == '0' && char_in(*och,"xX") && char_in(*(och+1),HEXDIG)) {
  303.     radix = 16;
  304.     limit = 2;
  305.     och += 1;
  306.   } else {
  307.     radix = 8;
  308.     limit = 3;
  309.     och = nch;
  310.   }
  311.  
  312.   while (limit-- > 0 && f_scan) {
  313.  
  314.     f_scan = FALSE;
  315.  
  316.     while ((int)(*nch)) *nch++ = *och++;
  317.  
  318.     nch = cmd+idx;
  319.     och = nch+1;
  320.  
  321.     switch (ch = *nch) {
  322.       case '0' :
  323.       case '1' :
  324.       case '2' :
  325.       case '3' :
  326.       case '4' :
  327.       case '5' :
  328.       case '6' :
  329.       case '7' :
  330.       case '8' :
  331.       case '9' :
  332.     if (ch == 9 && radix == 8) break;
  333.     accum = accum * radix + (int)(ch - '0');
  334.         f_scan = TRUE;
  335.     break;
  336.       case 'A' :
  337.       case 'B' :
  338.       case 'C' :
  339.       case 'D' :
  340.       case 'E' :
  341.       case 'F' :
  342.     if (radix == 8) break;
  343.     accum = accum * radix + (int)(ch - 'A') + 10;
  344.         f_scan = TRUE;
  345.     break;
  346.       case 'a' :
  347.       case 'b' :
  348.       case 'c' :
  349.       case 'd' :
  350.       case 'e' :
  351.       case 'f' :
  352.     if (radix == 8) break;
  353.     accum = accum * radix + (int)(ch - 'a') + 10;
  354.         f_scan = TRUE;
  355.     break;
  356.       default  : break;
  357.     }
  358.   }
  359.  
  360.   *pidx = idx;
  361.   return(accum);
  362. }
  363. /***************************************************************************/
  364. get_escape(char *cmd, int *pidx, char quote)
  365. /* Interpret escape'd (i.e., '\' (backslash) character */
  366. {
  367.   int   idx = *pidx;
  368.  
  369.   if (quote == '"') {
  370.     char *nch = cmd+idx;
  371.     char *och = nch+1;
  372.     char *xch = nch;
  373.     while ((int)(*nch)) *nch++ = *och++;
  374.     switch (*xch) {
  375.       case 'a' : *xch = '\a'; break;
  376.       case 'b' : *xch = '\b'; break;
  377.       case 'c' : *xch = '\c'; break;
  378.       case 'd' : *xch = '\d'; break;
  379.       case 'e' : *xch = '\e'; break;
  380.       case 'f' : *xch = '\f'; break;
  381.       case 'g' : *xch = '\g'; break;
  382.       case 'h' : *xch = '\h'; break;
  383.       case 'i' : *xch = '\i'; break;
  384.       case 'j' : *xch = '\j'; break;
  385.       case 'k' : *xch = '\k'; break;
  386.       case 'l' : *xch = '\l'; break;
  387.       case 'm' : *xch = '\m'; break;
  388.       case 'n' : *xch = '\n'; break;
  389.       case 'o' : *xch = '\o'; break;
  390.       case 'p' : *xch = '\p'; break;
  391.       case 'q' : *xch = '\q'; break;
  392.       case 'r' : *xch = '\r'; break;
  393.       case 's' : *xch = '\s'; break;
  394.       case 't' : *xch = '\t'; break;
  395.       case 'u' : *xch = '\u'; break;
  396.       case 'v' : *xch = '\v'; break;
  397.       case 'w' : *xch = '\w'; break;
  398.       case 'x' : *xch = '\x'; break;
  399.       case 'y' : *xch = '\y'; break;
  400.       case 'z' : *xch = '\z'; break;
  401.       case '0' :
  402.       case '1' :
  403.       case '2' :
  404.         *xch = get_num(cmd, &idx);
  405.         break;
  406.     }
  407.   }
  408.  
  409.   *pidx = idx + 1;
  410. }
  411. /***************************************************************************/
  412. get_qvalue(char *cmd, int idx, char quote, char **value)
  413. /* Extract a quoted value part from command line */
  414. {
  415.   char ch;
  416.   int  f_esc;
  417.  
  418.   *value = cmd + (++idx);
  419.  
  420.   do {
  421.     while ((int)(ch = cmd[idx]) && ch != '\\' && ch != quote) idx++;
  422.     if (!(int)ch) return(-1);
  423.     if (ch == '\\') {
  424.       f_esc = TRUE;
  425.       get_escape(cmd, &idx, quote);
  426.     } else f_esc = FALSE;
  427.   } while (f_esc);
  428.  
  429.   cmd[idx] = '\0';
  430.  
  431.   for (idx += 1; (int)(ch = cmd[idx]) && ch == ' '; idx++);
  432.  
  433.   if ((int)ch) return(-1);
  434.  
  435.   return(0);
  436. }
  437. /***************************************************************************/
  438. get_parm(char **name, char **value)
  439. /* Extract environment symbol name and value from command line */
  440. {
  441.     char ch;
  442.     int  idx;
  443.     int  sdx;
  444.  
  445.     static char cmd[128];
  446.  
  447.     get_cmdline(cmd);
  448.  
  449.     for (idx = 0; (int)(ch = cmd[idx]) && ch  == ' '; idx++);
  450.  
  451.     if (!(int)ch) return(1);
  452.  
  453.     *name = cmd + idx;
  454.  
  455.     for (; (int)(ch = cmd[idx]) && ch != '=' && ch != ' '; idx++);
  456.  
  457.     if (!(int)ch) return(-1);
  458.  
  459.     if (ch == ' ') {
  460.       cmd[idx] = '\0';
  461.       for (idx += 1; (int)(ch = cmd[idx]) && ch != '='; idx++);
  462.     } else cmd[idx] = '\0';
  463.  
  464.     if (!(int)ch) return(-1);
  465.  
  466.     for (sdx = (idx += 1); (int)(ch = cmd[idx]) && ch == ' '; idx++);
  467.  
  468.     /*if (!(int)ch) return(-1);*/
  469.  
  470.     switch (ch) {
  471.       case '"'  : return(get_qvalue(cmd, idx, '"', value));
  472.       case '\'' : return(get_qvalue(cmd, idx, '\'', value));
  473.       default   : *value = cmd + sdx; break;
  474.     }
  475.  
  476.     return(0);
  477. }
  478. /***************************************************************************/
  479. main(int argc,char **argv)
  480. {
  481.     unsigned env_seg[2];
  482.     char **s,**t;
  483.     int k;
  484.     long now;
  485.     struct tm *local;
  486.  
  487.     static char *name = NULL, *value = NULL;
  488.  
  489.     switch (get_parm(&name,&value)) {
  490.  
  491.     case -1:
  492.  
  493.     fprintf(stderr,"Invalid symbol definition syntax\n");
  494.     exit(-1);
  495.  
  496.     case 0:
  497.  
  498.         /* Find and read environment */
  499.  
  500.         find_env(env_seg);
  501.         read_env(env_seg[1],&k,&s,&t);
  502.  
  503.     /* Set the variable <name> to <value> */
  504.  
  505.     set_env_var(&k,&s,&t,name,value);
  506.  
  507.     /* Update caller's environment */
  508.  
  509.     write_env(env_seg[0],k,s,t);
  510.  
  511.     break;
  512.  
  513.     case 1:
  514.  
  515.     fprintf(stderr,"Usage: setenv <symbol name> = <value>\n");
  516.     break;
  517.     }
  518.  
  519.     return(0);
  520. }
  521. /***************************************************************************/
  522.  
  523.