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