aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/rc/var.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/rc/var.c')
-rw-r--r--src/cmd/rc/var.c336
1 files changed, 336 insertions, 0 deletions
diff --git a/src/cmd/rc/var.c b/src/cmd/rc/var.c
new file mode 100644
index 0000000..3e9635f
--- /dev/null
+++ b/src/cmd/rc/var.c
@@ -0,0 +1,336 @@
+#include "rc.h"
+#include "parse.h"
+
+// TODO: string interning
+
+// -----------------------------------------------------------------------
+// globals
+
+struct Keyword
+{
+ char *name;
+ int type;
+};
+
+static Var *globals[512];
+static struct Keyword keywords[100]; // sparse map means less hits
+
+// -----------------------------------------------------------------------
+// internals
+
+static
+int
+hash(char *s, int len)
+{
+ int h =0, i = 1;
+ while(*s)
+ h += *s++*i++;
+
+ h %= len;
+ return h < 0 ? h+len : h;
+}
+
+static
+void
+·setvar(char *name, Word *val, int call)
+{
+ Var *v = var(name);
+ freeword(v->val);
+
+ v->val = val;
+ v->new = 1; // this never turns off?
+
+ if(call && v->update)
+ v->update(v);
+}
+
+static
+char*
+list2strcolon(Word *words)
+{
+ char *value, *s, *t;
+ int len = 0;
+ Word *ap;
+ for(ap = words;ap;ap = ap->link)
+ len+=1+strlen(ap->str);
+
+ value = emalloc(len+1);
+
+ s = value;
+ for(ap = words; ap; ap = ap->link){
+ for(t = ap->str;*t;) *s++=*t++;
+ *s++=':';
+ }
+
+ if(s==value)
+ *s='\0';
+ else
+ s[-1]='\0';
+
+ return value;
+}
+
+static
+void
+littlepath(Var *v)
+{
+ /* convert $path to $PATH */
+ char *p;
+ Word *w;
+
+ p = list2strcolon(v->val);
+ w = emalloc(sizeof(*w));
+ w->str = p;
+ w->link = nil;
+
+ ·setvar("PATH", w, 1);
+}
+
+static
+void
+bigpath(Var *v)
+{
+ /* convert $PATH to $path */
+ char *p, *q;
+ Word **l, *w;
+
+ if(v->val == nil){
+ ·setvar("path", nil, 0);
+ return;
+ }
+
+ p = v->val->str;
+ w = nil;
+ l = &w;
+
+ /* Doesn't handle escaped colon nonsense. */
+ if(p[0] == 0)
+ p = nil;
+
+ while(p){
+ q = strchr(p, ':');
+ if(q)
+ *q = 0;
+
+ *l = makeword(p[0] ? p : ".", nil);
+ l = &(*l)->link;
+
+ if(q){
+ *q = ':';
+ p = q+1;
+ }else
+ p = nil;
+ }
+ ·setvar("path", w, 0);
+}
+
+// -----------------------------------------------------------------------
+// exports
+
+Var*
+makevar(char *name, Var *link)
+{
+ Var *v = emalloc(sizeof(*v));
+
+ v->name = name;
+ v->val = 0;
+ v->new = 0;
+ v->newfunc = 0;
+ v->link = link;
+ v->func = nil;
+ v->update = nil;
+
+ return v;
+}
+
+void
+setvar(char *name, Word *val)
+{
+ ·setvar(name, val, 1);
+}
+
+Var*
+definevar(char *name, Var *link)
+{
+ Var *v = emalloc(sizeof(*v));
+
+ v->name = name;
+ v->val = 0;
+ v->link = link;
+
+ return v;
+}
+
+Var*
+globalvar(char *name)
+{
+ int h;
+ Var *v;
+
+ h = hash(name, arrlen(globals));
+
+ if(strcmp(name,"PATH")==0){
+ flush(shell.err);
+ }
+
+ for(v = globals[h]; v; v = v->link){
+ if(strcmp(v->name, name) == 0){
+ return v;
+ }
+ }
+
+ return globals[h] = definevar(strdup(name), globals[h]);
+}
+
+Var*
+var(char *name)
+{
+ Var *v;
+ if(runner){
+ for(v = runner->local; v; v=v->link)
+ if(strcmp(v->name, name) == 0)
+ return v;
+ }
+ return globalvar(name);
+}
+
+static
+int
+cmpenv(const void *a, const void *b)
+{
+ return strcmp(*(char**)a, *(char**)b);
+}
+
+char**
+mkenv(void)
+{
+ char **env, **ep, *p, *q;
+ Var **h, *v;
+ Word *a;
+ int nvar=0, nchr=0, sep;
+
+#define BODY \
+ if((v==var(v->name)) && v->val){ \
+ nvar++; \
+ nchr+=strlen(v->name)+1; \
+ for(a=v->val;a;a=a->link) \
+ nchr+=strlen(a->str)+1; \
+ }
+
+ for(v= runner->local; v; v=v->link){
+ BODY
+ }
+ for(h=globals; h!=arrend(globals); h++){
+ for(v = *h; v; v=v->link){
+ BODY
+ }
+ }
+
+#undef BODY
+
+ env=emalloc((nvar+1)*sizeof(*env)+nchr);
+ ep=env;
+ p=(char *)&env[nvar+1];
+
+#define BODY \
+ if((v==var(v->name)) && v->val){ \
+ *ep++=p; \
+ q=v->name; \
+ while(*q) \
+ *p++=*q++; \
+ sep='='; \
+ for(a=v->val;a;a=a->link){ \
+ *p++=sep; \
+ sep='\1'; \
+ q=a->str; \
+ while(*q) \
+ *p++=*q++; \
+ } \
+ *p++='\0'; \
+ }
+
+ for(v=runner->local; v; v=v->link){
+ BODY
+ }
+ for(h=globals; h!=arrend(globals); h++){
+ for(v = *h; v; v=v->link){
+ BODY
+ }
+ }
+#undef BODY
+
+ *ep=0;
+
+ qsort((char *)env, nvar, sizeof ep[0], cmpenv);
+ return env;
+}
+
+void
+initpath(void)
+{
+ Var *v;
+
+ v = globalvar("path");
+ v->update = littlepath;
+
+ v = globalvar("PATH");
+ v->update = bigpath;
+
+ flush(shell.err);
+ bigpath(v);
+}
+
+#define KEYWORDS \
+ KEYWORD("for", Tfor) \
+ KEYWORD("in", Tin) \
+ KEYWORD("while", Twhile) \
+ KEYWORD("if", Tif) \
+ KEYWORD("else", Telse) \
+ KEYWORD("switch", Tswitch) \
+ KEYWORD("case", Tcase) \
+ KEYWORD("!", Tbang) \
+ KEYWORD("@", Tsubshell) \
+ KEYWORD("func", Tfunc)
+
+void
+initkeywords(void)
+{
+ int i, s, j, h;
+#define KEYWORD(a, b) a,
+ static char *name[] = { KEYWORDS };
+#undef KEYWORD
+#define KEYWORD(a, b) b,
+ static int type[] = { KEYWORDS };
+#undef KEYWORD
+
+ for(i = 0; i < arrlen(type); i++){
+ h = hash(name[i], arrlen(keywords));
+ for(s=0; s < arrlen(keywords); s++){
+ j = (h + s) % arrlen(keywords);
+ if(!keywords[j].type || strcmp(keywords[j].name, name[i]) == 0){
+ keywords[j].name = name[i];
+ keywords[j].type = type[i];
+ goto nextkeyword;
+ }
+ }
+ nextkeyword:;
+ }
+}
+
+int
+iskeyword(char *word)
+{
+ int i, s, h;
+
+ h = hash(word, arrlen(keywords));
+ for(s = 0; s < arrlen(keywords); s++){
+ i = (h + s) % arrlen(keywords);
+ if(!keywords[i].type)
+ return 0;
+ if(strcmp(keywords[i].name, word) == 0)
+ return keywords[i].type;
+ }
+ return 0;
+}
+
+#undef KEYWORDS