]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/rc/simple.c
rc: fix inband globbing bugs, cleanup
[plan9front.git] / sys / src / cmd / rc / simple.c
1 /*
2  * Maybe `simple' is a misnomer.
3  */
4 #include "rc.h"
5 #include "getflags.h"
6 #include "exec.h"
7 #include "io.h"
8 #include "fns.h"
9 /*
10  * Search through the following code to see if we're just going to exit.
11  */
12 int
13 exitnext(void){
14         union code *c=&runq->code[runq->pc];
15         while(c->f==Xpopredir) c++;
16         return c->f==Xexit;
17 }
18
19 void
20 Xsimple(void)
21 {
22         word *a;
23         var *v;
24         struct builtin *bp;
25         int pid;
26
27         a = globlist(runq->argv->words);
28         if(a==0){
29                 Xerror1("empty argument list");
30                 return;
31         }
32         if(flag['x'])
33                 pfmt(err, "%v\n", a); /* wrong, should do redirs */
34         v = gvlook(a->word);
35         if(v->fn)
36                 execfunc(v);
37         else{
38                 if(strcmp(a->word, "builtin")==0){
39                         if(count(a)==1){
40                                 pfmt(err, "builtin: empty argument list\n");
41                                 setstatus("empty arg list");
42                                 poplist();
43                                 return;
44                         }
45                         a = a->next;
46                         popword();
47                 }
48                 for(bp = Builtin;bp->name;bp++)
49                         if(strcmp(a->word, bp->name)==0){
50                                 (*bp->fnc)();
51                                 return;
52                         }
53                 if(exitnext()){
54                         /* fork and wait is redundant */
55                         pushword("exec");
56                         execexec();
57                 }
58                 else{
59                         flush(err);
60                         Updenv();       /* necessary so changes don't go out again */
61                         if((pid = execforkexec()) < 0){
62                                 Xerror("try again");
63                                 return;
64                         }
65
66                         /* interrupts don't get us out */
67                         poplist();
68                         while(Waitfor(pid, 1) < 0)
69                                 ;
70                 }
71         }
72 }
73 struct word nullpath = { "", 0};
74
75 void
76 doredir(redir *rp)
77 {
78         if(rp){
79                 doredir(rp->next);
80                 switch(rp->type){
81                 case ROPEN:
82                         if(rp->from!=rp->to){
83                                 Dup(rp->from, rp->to);
84                                 close(rp->from);
85                         }
86                         break;
87                 case RDUP:
88                         Dup(rp->from, rp->to);
89                         break;
90                 case RCLOSE:
91                         close(rp->from);
92                         break;
93                 }
94         }
95 }
96
97 word*
98 searchpath(char *w)
99 {
100         word *path;
101         if(strncmp(w, "/", 1)==0
102         || strncmp(w, "#", 1)==0
103         || strncmp(w, "./", 2)==0
104         || strncmp(w, "../", 3)==0
105         || (path = vlook("path")->val)==0)
106                 path=&nullpath;
107         return path;
108 }
109
110 void
111 execexec(void)
112 {
113         popword();      /* "exec" */
114         if(runq->argv->words==0){
115                 Xerror1("empty argument list");
116                 return;
117         }
118         doredir(runq->redir);
119         Execute(runq->argv->words, searchpath(runq->argv->words->word));
120         poplist();
121         Xexit();
122 }
123
124 void
125 execfunc(var *func)
126 {
127         word *starval;
128         popword();
129         starval = runq->argv->words;
130         runq->argv->words = 0;
131         poplist();
132         start(func->fn, func->pc, runq->local);
133         runq->local = newvar("*", runq->local);
134         runq->local->val = starval;
135         runq->local->changed = 1;
136 }
137
138 int
139 dochdir(char *word)
140 {
141         /* report to /dev/wdir if it exists and we're interactive */
142         if(chdir(word)<0) return -1;
143         if(flag['i']!=0){
144                 static int wdirfd = -2;
145                 if(wdirfd==-2)  /* try only once */
146                         wdirfd = open("/dev/wdir", OWRITE|OCEXEC);
147                 if(wdirfd>=0)
148                         write(wdirfd, word, strlen(word));
149         }
150         return 1;
151 }
152
153 void
154 execcd(void)
155 {
156         word *a = runq->argv->words;
157         word *cdpath;
158         char *dir;
159
160         setstatus("can't cd");
161         cdpath = vlook("cdpath")->val;
162         switch(count(a)){
163         default:
164                 pfmt(err, "Usage: cd [directory]\n");
165                 break;
166         case 2:
167                 if(a->next->word[0]=='/' || cdpath==0)
168                         cdpath = &nullpath;
169                 for(; cdpath; cdpath = cdpath->next){
170                         if(cdpath->word[0] != '\0')
171                                 dir = smprint("%s/%s", cdpath->word,
172                                         a->next->word);
173                         else
174                                 dir = estrdup(a->next->word);
175
176                         if(dochdir(dir) >= 0){
177                                 if(cdpath->word[0] != '\0' &&
178                                     strcmp(cdpath->word, ".") != 0)
179                                         pfmt(err, "%s\n", dir);
180                                 free(dir);
181                                 setstatus("");
182                                 break;
183                         }
184                         free(dir);
185                 }
186                 if(cdpath==0)
187                         pfmt(err, "Can't cd %s: %r\n", a->next->word);
188                 break;
189         case 1:
190                 a = vlook("home")->val;
191                 if(count(a)>=1){
192                         if(dochdir(a->word)>=0)
193                                 setstatus("");
194                         else
195                                 pfmt(err, "Can't cd %s: %r\n", a->word);
196                 }
197                 else
198                         pfmt(err, "Can't cd -- $home empty\n");
199                 break;
200         }
201         poplist();
202 }
203
204 void
205 execexit(void)
206 {
207         switch(count(runq->argv->words)){
208         default:
209                 pfmt(err, "Usage: exit [status]\nExiting anyway\n");
210         case 2:
211                 setstatus(runq->argv->words->next->word);
212         case 1: Xexit();
213         }
214 }
215
216 void
217 execshift(void)
218 {
219         int n;
220         word *a;
221         var *star;
222         switch(count(runq->argv->words)){
223         default:
224                 pfmt(err, "Usage: shift [n]\n");
225                 setstatus("shift usage");
226                 poplist();
227                 return;
228         case 2:
229                 n = atoi(runq->argv->words->next->word);
230                 break;
231         case 1:
232                 n = 1;
233                 break;
234         }
235         star = vlook("*");
236         for(;star->val;--n){
237                 a = star->val->next;
238                 efree(star->val->word);
239                 efree(star->val);
240                 star->val = a;
241                 star->changed = 1;
242         }
243         setstatus("");
244         poplist();
245 }
246
247 int
248 mapfd(int fd)
249 {
250         redir *rp;
251         for(rp = runq->redir;rp;rp = rp->next){
252                 switch(rp->type){
253                 case RCLOSE:
254                         if(rp->from==fd)
255                                 fd=-1;
256                         break;
257                 case RDUP:
258                 case ROPEN:
259                         if(rp->to==fd)
260                                 fd = rp->from;
261                         break;
262                 }
263         }
264         return fd;
265 }
266 union code rdcmds[4];
267
268 void
269 execcmds(io *f)
270 {
271         static int first = 1;
272         if(first){
273                 rdcmds[0].i = 1;
274                 rdcmds[1].f = Xrdcmds;
275                 rdcmds[2].f = Xreturn;
276                 first = 0;
277         }
278         start(rdcmds, 1, runq->local);
279         runq->cmdfd = f;
280         runq->iflast = 0;
281 }
282
283 void
284 execeval(void)
285 {
286         char *cmdline;
287         int len;
288         if(count(runq->argv->words)<=1){
289                 Xerror1("Usage: eval cmd ...");
290                 return;
291         }
292         eflagok = 1;
293         cmdline = list2str(runq->argv->words->next);
294         len = strlen(cmdline);
295         cmdline[len] = '\n';
296         poplist();
297         execcmds(opencore(cmdline, len+1));
298         efree(cmdline);
299 }
300 union code dotcmds[14];
301
302 void
303 execdot(void)
304 {
305         int iflag = 0;
306         int fd;
307         list *av;
308         thread *p = runq;
309         char *zero, *file;
310         word *path;
311         static int first = 1;
312
313         if(first){
314                 dotcmds[0].i = 1;
315                 dotcmds[1].f = Xmark;
316                 dotcmds[2].f = Xword;
317                 dotcmds[3].s="0";
318                 dotcmds[4].f = Xlocal;
319                 dotcmds[5].f = Xmark;
320                 dotcmds[6].f = Xword;
321                 dotcmds[7].s="*";
322                 dotcmds[8].f = Xlocal;
323                 dotcmds[9].f = Xrdcmds;
324                 dotcmds[10].f = Xunlocal;
325                 dotcmds[11].f = Xunlocal;
326                 dotcmds[12].f = Xreturn;
327                 first = 0;
328         }
329         else
330                 eflagok = 1;
331         popword();
332         if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){
333                 iflag = 1;
334                 popword();
335         }
336         /* get input file */
337         if(p->argv->words==0){
338                 Xerror1("Usage: . [-i] file [arg ...]");
339                 return;
340         }
341         zero = estrdup(p->argv->words->word);
342         popword();
343         fd = -1;
344         for(path = searchpath(zero); path; path = path->next){
345                 if(path->word[0] != '\0')
346                         file = smprint("%s/%s", path->word, zero);
347                 else
348                         file = estrdup(zero);
349
350                 fd = open(file, 0);
351                 free(file);
352                 if(fd >= 0)
353                         break;
354                 if(strcmp(file, "/dev/stdin")==0){      /* for sun & ucb */
355                         fd = Dup1(0);
356                         if(fd>=0)
357                                 break;
358                 }
359         }
360         if(fd<0){
361                 pfmt(err, "%s: ", zero);
362                 setstatus("can't open");
363                 Xerror(".: can't open");
364                 return;
365         }
366         /* set up for a new command loop */
367         start(dotcmds, 1, (struct var *)0);
368         pushredir(RCLOSE, fd, 0);
369         runq->cmdfile = zero;
370         runq->cmdfd = openfd(fd);
371         runq->iflag = iflag;
372         runq->iflast = 0;
373         /* push $* value */
374         pushlist();
375         runq->argv->words = p->argv->words;
376         /* free caller's copy of $* */
377         av = p->argv;
378         p->argv = av->next;
379         efree(av);
380         /* push $0 value */
381         pushlist();
382         pushword(zero);
383         ndot++;
384 }
385
386 void
387 execflag(void)
388 {
389         char *letter, *val;
390         switch(count(runq->argv->words)){
391         case 2:
392                 setstatus(flag[(uchar)runq->argv->words->next->word[0]]?"":"flag not set");
393                 break;
394         case 3:
395                 letter = runq->argv->words->next->word;
396                 val = runq->argv->words->next->next->word;
397                 if(strlen(letter)==1){
398                         if(strcmp(val, "+")==0){
399                                 flag[(uchar)letter[0]] = flagset;
400                                 break;
401                         }
402                         if(strcmp(val, "-")==0){
403                                 flag[(uchar)letter[0]] = 0;
404                                 break;
405                         }
406                 }
407         default:
408                 Xerror1("Usage: flag [letter] [+-]");
409                 return;
410         }
411         poplist();
412 }
413
414 void
415 execwhatis(void){       /* mildly wrong -- should fork before writing */
416         word *a, *b, *path;
417         var *v;
418         struct builtin *bp;
419         char *file;
420         struct io out[1];
421         int found, sep;
422         a = runq->argv->words->next;
423         if(a==0){
424                 Xerror1("Usage: whatis name ...");
425                 return;
426         }
427         setstatus("");
428         out->fd = mapfd(1);
429         out->bufp = out->buf;
430         out->ebuf = &out->buf[NBUF];
431         out->strp = 0;
432         for(;a;a = a->next){
433                 v = vlook(a->word);
434                 if(v->val){
435                         pfmt(out, "%s=", a->word);
436                         if(v->val->next==0)
437                                 pfmt(out, "%q\n", v->val->word);
438                         else{
439                                 sep='(';
440                                 for(b = v->val;b && b->word;b = b->next){
441                                         pfmt(out, "%c%q", sep, b->word);
442                                         sep=' ';
443                                 }
444                                 pfmt(out, ")\n");
445                         }
446                         found = 1;
447                 }
448                 else
449                         found = 0;
450                 v = gvlook(a->word);
451                 if(v->fn)
452                         pfmt(out, "fn %q %s\n", v->name, v->fn[v->pc-1].s);
453                 else{
454                         for(bp = Builtin;bp->name;bp++)
455                                 if(strcmp(a->word, bp->name)==0){
456                                         pfmt(out, "builtin %s\n", a->word);
457                                         break;
458                                 }
459                         if(!bp->name){
460                                 for(path = searchpath(a->word); path;
461                                     path = path->next){
462                                         if(path->word[0] != '\0')
463                                                 file = smprint("%s/%s",
464                                                         path->word, a->word);
465                                         else
466                                                 file = estrdup(a->word);
467                                         if(Executable(file)){
468                                                 pfmt(out, "%s\n", file);
469                                                 free(file);
470                                                 break;
471                                         }
472                                         free(file);
473                                 }
474                                 if(!path && !found){
475                                         pfmt(err, "%s: not found\n", a->word);
476                                         setstatus("not found");
477                                 }
478                         }
479                 }
480         }
481         poplist();
482         flush(err);
483 }
484
485 void
486 execwait(void)
487 {
488         switch(count(runq->argv->words)){
489         default:
490                 Xerror1("Usage: wait [pid]");
491                 return;
492         case 2:
493                 Waitfor(atoi(runq->argv->words->next->word), 0);
494                 break;
495         case 1:
496                 Waitfor(-1, 0);
497                 break;
498         }
499         poplist();
500 }