home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 18 REXX / 18-REXX.zip / rximc175.zip / shell.c < prev    next >
Text File  |  2002-08-06  |  9KB  |  232 lines

  1. /* A Command Shell for REXX/imc                 (C) Ian Collier 1992 */
  2.  
  3. #include<stdio.h>
  4. #include<stdlib.h>
  5. #include<unistd.h>
  6. #include<errno.h>
  7. #include<setjmp.h>
  8. #include<sys/types.h>
  9. #include<sys/wait.h>
  10. #include<sys/param.h>
  11. #include"const.h"
  12. #include"functions.h"
  13.  
  14. typedef struct _hashitem {  /* An item in the hash table of path names */
  15.    struct _hashitem *next;  /* The next item in the bucket */
  16.    int hits;                /* Number of times this has been found */
  17.    int expense;             /* Position within $PATH */
  18.    int dot;                 /* Whether dot occurred in the path before this */
  19.    int data;                /* Offset from end of header to data */
  20. } hashitem;
  21.  
  22. static unsigned hashfn();       /* the hash function to make int from string */
  23. static hashitem **hashcreate(); /* Create a hash table */
  24. static void *search();          /* search in the hash table */
  25. static char *locate();          /* find out the path for a command */
  26. static void hashcmd();          /* Execute the "hash" builtin command */
  27. static void hashdel();          /* Delete an element from the hash table */
  28.  
  29. char **arguments=0; /* An array to hold the argument list */
  30. unsigned argnum=0;  /* The number of elements allocated so far */
  31. hashitem **hashtable;
  32. int hashbuckets;
  33.  
  34. int shell(command) /* Execute a UNIX command.  The command must be writable */
  35. char *command;     /* and zero-terminated */
  36. {
  37.    int argc=0;
  38.    char quote=0;
  39.    char c;
  40.    int i,j;
  41.    int pid;
  42.    char *exec;
  43.  
  44.    if(!arguments)                   /* Allocate some initial memory */
  45.       arguments=(char**)allocm((argnum=20)*sizeof(char*)),
  46.       hashtable=hashcreate(hashbuckets=251);
  47.    while(command[0]==' ')command++; /* Ignore leading spaces */
  48.    arguments[argc++]=command;       /* Store the start of arg[0] */
  49.    for(i=j=0;c=command[j];j++){     /* Start tokenising... */
  50.       if(c==quote){quote=0;continue;}
  51.       if(quote){command[i++]=c;continue;}
  52.       if(c=='\''||c=='\"'){quote=c;continue;}
  53.       if(c==' '){
  54.          command[i++]=0;
  55.      while(command[++j]==' ');
  56.      j--;
  57.      if(argc+1>=argnum){
  58.         arguments=(char**)realloc((char*)arguments,
  59.            sizeof(char*)*(argnum+=10));
  60.         if(!arguments)die(Emem);
  61.      }
  62.      arguments[argc++]=command+i;
  63.      continue;
  64.       }
  65.       command[i++]=c;
  66.    }
  67.    command[i++]=0;                 /* 0-terminate the last argument */
  68.    if(!arguments[argc-1][0])argc--;/* In case there were trailing spaces */
  69.    if(!argc)return 0;              /* Null string: just return */
  70.    arguments[argc++]=0;            /* Add the terminating NULL */
  71.    if(!strcmp(arguments[0],"hash"))/* "hash" is built in */
  72.       return hashcmd(arguments),0;
  73.    exec=locate(arguments[0]);      /* Locate the command */
  74.    if(!(pid=vfork())){
  75.       execv(exec,arguments);       /* Execute command */
  76.       if(errno==ENOENT)            /* did not exist */
  77.          fprintf(stderr,"%s: Command not found.\n",arguments[0]);
  78.       else perror(exec);           /* some other error */
  79.       _exit(-3);
  80.    }
  81.    if(pid==-1){
  82.       perror("vfork");
  83.       return -3;
  84.    }
  85.    i=0;
  86.    waitpid(pid,&i,0);             /* Wait for command */
  87.    return (int)(char)(i/256);
  88. }
  89.  
  90. static unsigned hashfn(string,buckets)  /* A hash function */
  91. char *string;         /* the string to hash */
  92. int buckets;          /* the number of hash buckets */
  93. {
  94.    register unsigned i=0;
  95.    while(*string)i+=(i<<3)+*string++;
  96. /* return (((i*40503L)&65535)*buckets)/65536;*/ /* multiplicative hashing: when
  97.                                                  buckets is a power of 2 */
  98.    return i%buckets;        /* division method: when buckets is a prime such
  99.                             that 16^k=a(mod buckets) for small k and a */
  100. }
  101.  
  102. static hashitem **hashcreate(buckets)
  103.                           /* Create hash table as array of null pointers */
  104. int buckets;              /* Number of buckets in hash table */
  105. {
  106.    hashitem **table=(hashitem**)allocm(buckets*sizeof(char *));
  107.    int i;
  108.    for(i=0;i<buckets;table[i++]=0);
  109.    return table;
  110. }
  111.  
  112. static void *search(name,exist) /* Search for a name in the hash table    */
  113. char *name;                     /* if exist=1, the result is a pointer to */
  114. int *exist;                     /* the item; if exist=0 the result is a   */
  115.                                 /* "next" field where the new item would  */
  116. {                               /* be inserted                            */
  117.    int h=hashfn(name,hashbuckets);
  118.    hashitem **i=&hashtable[h];
  119.    hashitem *j;
  120.    int c;
  121.    if(!(j=*i)) return *exist=0,(void *)i; /* No elements in this bucket */
  122.    while(c=strcmp(name,(char *)(j+1))){   /* stop when correct element found */
  123.       if(c<0) return *exist=0,(void *)i;  /* gone too far down the list */
  124.       i=&(j->next);
  125.       if(!(j=*i)) return *exist=0,(void *)i; /* no next element in list */
  126.    }
  127.    return *exist=1,(void *)j;
  128. }
  129.  
  130. static char *locate(name)  /* Locate the executable file "name" */
  131. char *name;
  132. {
  133.    char *path=getenv("PATH");
  134.    void *hash;
  135.    int exist;
  136.    int dot=0;
  137.    int i;
  138.    int dirs=0;
  139.    hashitem *new,**old;
  140.    char *ans;
  141.    static char test[MAXPATHLEN+1];
  142.    if(!strchr(name,'/')){             /* only search if the name has no '/'s */
  143.       hash=search(name,&exist);       /* first search the hash table */
  144.       if(!exist&&path) while(path[0]){/* then search the path */
  145.          dirs++;
  146.          for(i=0;(test[i]=path[0])&&path++[0]!=':';i++); /* Copy next dir */
  147.      if(i==1&&test[0]=='.'){dot=1;continue;} /* Test for "." */
  148.      test[i]='/';
  149.      strcpy(test+i+1,name);                  /* add slash and name */
  150.      if(!access(test,X_OK)){                 /* if it is executable... */
  151.         new=(hashitem *)                     /* make a new hash item */
  152.            allocm(sizeof(hashitem)+strlen(name)+strlen(test)+2);
  153.         old=(hashitem **)hash; /* this points to the previous link field */
  154.             new->next=*old;
  155.         *old=new;
  156.         new->dot=dot;
  157.         new->hits=0;
  158.         new->expense=dirs;
  159.         new->data=strlen(name)+1;
  160.         strcpy((char *)(new+1),name);
  161.         strcpy((char *)(new+1)+new->data,test);
  162.         exist=1;
  163.         hash=(void*)new;
  164.         break;
  165.      }
  166.       }
  167.       if(exist){ /* Now, if the hash item was found or newly created, use it */
  168.          new=(hashitem *)hash;
  169.      new->hits++;
  170.      ans=(char *)(new+1)+new->data;
  171.      if(new->dot&&!access(name,X_OK)) /* If "." came in the path before */
  172.         return name;                  /* the named directory, check "." */
  173.      return ans;                      /* first, then return the stored  */
  174.       }                                   /* name.                          */
  175.    }
  176.    return name; /* if the name contains '/' or wasn't found in the path,
  177.                    return it unchanged. */
  178. }
  179.  
  180. static void hashdel(name)  /* delete name from hash table, if present */
  181. char *name;
  182. {
  183.    int h=hashfn(name,hashbuckets);
  184.    hashitem **i=&hashtable[h];
  185.    hashitem *j;
  186.    int c;
  187.    if(!(j=*i)) return;                   /* No items in this bucket */
  188.    while(c=strcmp(name,(char *)(j+1))){  /* search for the given name */
  189.       if(c<0) return;
  190.       i=&(j->next);
  191.       if(!(j=*i)) return;
  192.    }
  193.    *i=j->next;                        /* link the next item to the previous  */
  194.    free(j);                           /* so deleting this one from the chain */
  195. }
  196.  
  197. void hashclear()          /* Clear the hash table (eg when PATH changes) */
  198. {
  199.    int j;
  200.    hashitem *h,*k;
  201.    for(j=0;j<hashbuckets;j++)
  202.       for(h=hashtable[j],hashtable[j]=0;h;h=k){
  203.          k=h->next;        /* find the address of the next item before */
  204.          free((char *)h);  /* freeing this one (obviously).            */
  205.       }
  206. }
  207.  
  208. static void hashcmd(args) /* Implement the "hash" builtin command */
  209. char *args[];
  210. {
  211.    int i;
  212.    int j;
  213.    int hits;
  214.    hashitem *h;
  215.    if(args[i=1])          /* some arguments exist */
  216.       while(args[i]){
  217.          if(!strcmp(args[i],"-r")) hashclear();  /* Clear table */
  218.      else locate(args[i]);      /* add argument to table */
  219.      i++;
  220.       }
  221.    else{                  /* no arguments: print table */
  222.       hits=0;
  223.       for(i=0;i<hashbuckets;i++)
  224.          for(j=0,h=hashtable[i];h;h=h->next){
  225.         if(!hits++) puts(" hits    cost    command");
  226.         putchar(j++?'+':' ');
  227.             printf("%-7d %-7d %s\n",h->hits,h->expense,(char*)(h+1)+h->data);
  228.      }
  229.       if(!hits) puts("No commands in hash table.");
  230.    }
  231. }
  232.