simple.c - 9base - revived minimalist port of Plan 9 userland to Unix | |
git clone git://git.suckless.org/9base | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
simple.c (9019B) | |
--- | |
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 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 r… | |
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\… | |
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 g… | |
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 | |
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* | |
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 | |
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 | |
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 | |
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 write(wdirfd, word, strlen(word)); | |
150 } | |
151 return 1; | |
152 } | |
153 | |
154 void | |
155 execcd(void) | |
156 { | |
157 word *a = runq->argv->words; | |
158 word *cdpath; | |
159 char dir[512]; | |
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 strcpy(dir, cdpath->word); | |
171 if(dir[0]) | |
172 strcat(dir, "/"); | |
173 strcat(dir, a->next->word); | |
174 if(dochdir(dir)>=0){ | |
175 if(strlen(cdpath->word) | |
176 && strcmp(cdpath->word, ".")!=0) | |
177 pfmt(err, "%s\n", dir); | |
178 setstatus(""); | |
179 break; | |
180 } | |
181 } | |
182 if(cdpath==0) | |
183 pfmt(err, "Can't cd %s: %r\n", a->next->word); | |
184 break; | |
185 case 1: | |
186 a = vlook("home")->val; | |
187 if(count(a)>=1){ | |
188 if(dochdir(a->word)>=0) | |
189 setstatus(""); | |
190 else | |
191 pfmt(err, "Can't cd %s: %r\n", a->word); | |
192 } | |
193 else | |
194 pfmt(err, "Can't cd -- $home empty\n"); | |
195 break; | |
196 } | |
197 poplist(); | |
198 } | |
199 | |
200 void | |
201 execexit(void) | |
202 { | |
203 switch(count(runq->argv->words)){ | |
204 default: | |
205 pfmt(err, "Usage: exit [status]\nExiting anyway\n"); | |
206 case 2: | |
207 setstatus(runq->argv->words->next->word); | |
208 case 1: Xexit(); | |
209 } | |
210 } | |
211 | |
212 void | |
213 execshift(void) | |
214 { | |
215 int n; | |
216 word *a; | |
217 var *star; | |
218 switch(count(runq->argv->words)){ | |
219 default: | |
220 pfmt(err, "Usage: shift [n]\n"); | |
221 setstatus("shift usage"); | |
222 poplist(); | |
223 return; | |
224 case 2: | |
225 n = atoi(runq->argv->words->next->word); | |
226 break; | |
227 case 1: | |
228 n = 1; | |
229 break; | |
230 } | |
231 star = vlook("*"); | |
232 for(;n && star->val;--n){ | |
233 a = star->val->next; | |
234 efree(star->val->word); | |
235 efree((char *)star->val); | |
236 star->val = a; | |
237 star->changed = 1; | |
238 } | |
239 setstatus(""); | |
240 poplist(); | |
241 } | |
242 | |
243 int | |
244 octal(char *s) | |
245 { | |
246 int n = 0; | |
247 while(*s==' ' || *s=='\t' || *s=='\n') s++; | |
248 while('0'<=*s && *s<='7') n = n*8+*s++-'0'; | |
249 return n; | |
250 } | |
251 | |
252 int | |
253 mapfd(int fd) | |
254 { | |
255 redir *rp; | |
256 for(rp = runq->redir;rp;rp = rp->next){ | |
257 switch(rp->type){ | |
258 case RCLOSE: | |
259 if(rp->from==fd) | |
260 fd=-1; | |
261 break; | |
262 case RDUP: | |
263 case ROPEN: | |
264 if(rp->to==fd) | |
265 fd = rp->from; | |
266 break; | |
267 } | |
268 } | |
269 return fd; | |
270 } | |
271 union code rdcmds[4]; | |
272 | |
273 void | |
274 execcmds(io *f) | |
275 { | |
276 static int first = 1; | |
277 if(first){ | |
278 rdcmds[0].i = 1; | |
279 rdcmds[1].f = Xrdcmds; | |
280 rdcmds[2].f = Xreturn; | |
281 first = 0; | |
282 } | |
283 start(rdcmds, 1, runq->local); | |
284 runq->cmdfd = f; | |
285 runq->iflast = 0; | |
286 } | |
287 | |
288 void | |
289 execeval(void) | |
290 { | |
291 char *cmdline, *s, *t; | |
292 int len = 0; | |
293 word *ap; | |
294 if(count(runq->argv->words)<=1){ | |
295 Xerror1("Usage: eval cmd ..."); | |
296 return; | |
297 } | |
298 eflagok = 1; | |
299 for(ap = runq->argv->words->next;ap;ap = ap->next) | |
300 len+=1+strlen(ap->word); | |
301 cmdline = emalloc(len); | |
302 s = cmdline; | |
303 for(ap = runq->argv->words->next;ap;ap = ap->next){ | |
304 for(t = ap->word;*t;) *s++=*t++; | |
305 *s++=' '; | |
306 } | |
307 s[-1]='\n'; | |
308 poplist(); | |
309 execcmds(opencore(cmdline, len)); | |
310 efree(cmdline); | |
311 } | |
312 union code dotcmds[14]; | |
313 | |
314 void | |
315 execdot(void) | |
316 { | |
317 int iflag = 0; | |
318 int fd; | |
319 list *av; | |
320 thread *p = runq; | |
321 char *zero; | |
322 static int first = 1; | |
323 char file[512]; | |
324 word *path; | |
325 if(first){ | |
326 dotcmds[0].i = 1; | |
327 dotcmds[1].f = Xmark; | |
328 dotcmds[2].f = Xword; | |
329 dotcmds[3].s="0"; | |
330 dotcmds[4].f = Xlocal; | |
331 dotcmds[5].f = Xmark; | |
332 dotcmds[6].f = Xword; | |
333 dotcmds[7].s="*"; | |
334 dotcmds[8].f = Xlocal; | |
335 dotcmds[9].f = Xrdcmds; | |
336 dotcmds[10].f = Xunlocal; | |
337 dotcmds[11].f = Xunlocal; | |
338 dotcmds[12].f = Xreturn; | |
339 first = 0; | |
340 } | |
341 else | |
342 eflagok = 1; | |
343 popword(); | |
344 if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){ | |
345 iflag = 1; | |
346 popword(); | |
347 } | |
348 /* get input file */ | |
349 if(p->argv->words==0){ | |
350 Xerror1("Usage: . [-i] file [arg ...]"); | |
351 return; | |
352 } | |
353 zero = strdup(p->argv->words->word); | |
354 popword(); | |
355 fd=-1; | |
356 for(path = searchpath(zero);path;path = path->next){ | |
357 strcpy(file, path->word); | |
358 if(file[0]) | |
359 strcat(file, "/"); | |
360 strcat(file, zero); | |
361 if((fd = open(file, 0))>=0) break; | |
362 if(strcmp(file, "/dev/stdin")==0){ /* for sun & u… | |
363 fd = Dup1(0); | |
364 if(fd>=0) | |
365 break; | |
366 } | |
367 } | |
368 if(fd<0){ | |
369 pfmt(err, "%s: ", zero); | |
370 setstatus("can't open"); | |
371 Xerror(".: can't open"); | |
372 return; | |
373 } | |
374 /* set up for a new command loop */ | |
375 start(dotcmds, 1, (struct var *)0); | |
376 pushredir(RCLOSE, fd, 0); | |
377 runq->cmdfile = zero; | |
378 runq->cmdfd = openfd(fd); | |
379 runq->iflag = iflag; | |
380 runq->iflast = 0; | |
381 /* push $* value */ | |
382 pushlist(); | |
383 runq->argv->words = p->argv->words; | |
384 /* free caller's copy of $* */ | |
385 av = p->argv; | |
386 p->argv = av->next; | |
387 efree((char *)av); | |
388 /* push $0 value */ | |
389 pushlist(); | |
390 pushword(zero); | |
391 ndot++; | |
392 } | |
393 | |
394 void | |
395 execflag(void) | |
396 { | |
397 char *letter, *val; | |
398 switch(count(runq->argv->words)){ | |
399 case 2: | |
400 setstatus(flag[(uchar)runq->argv->words->next->word[0]]?… | |
401 break; | |
402 case 3: | |
403 letter = runq->argv->words->next->word; | |
404 val = runq->argv->words->next->next->word; | |
405 if(strlen(letter)==1){ | |
406 if(strcmp(val, "+")==0){ | |
407 flag[(uchar)letter[0]] = flagset; | |
408 break; | |
409 } | |
410 if(strcmp(val, "-")==0){ | |
411 flag[(uchar)letter[0]] = 0; | |
412 break; | |
413 } | |
414 } | |
415 default: | |
416 Xerror1("Usage: flag [letter] [+-]"); | |
417 return; | |
418 } | |
419 poplist(); | |
420 } | |
421 | |
422 void | |
423 execwhatis(void){ /* mildly wrong -- should fork before writing */ | |
424 word *a, *b, *path; | |
425 var *v; | |
426 struct builtin *bp; | |
427 char file[512]; | |
428 struct io out[1]; | |
429 int found, sep; | |
430 a = runq->argv->words->next; | |
431 if(a==0){ | |
432 Xerror1("Usage: whatis name ..."); | |
433 return; | |
434 } | |
435 setstatus(""); | |
436 out->fd = mapfd(1); | |
437 out->bufp = out->buf; | |
438 out->ebuf = &out->buf[NBUF]; | |
439 out->strp = 0; | |
440 for(;a;a = a->next){ | |
441 v = vlook(a->word); | |
442 if(v->val){ | |
443 pfmt(out, "%s=", a->word); | |
444 if(v->val->next==0) | |
445 pfmt(out, "%q\n", v->val->word); | |
446 else{ | |
447 sep='('; | |
448 for(b = v->val;b && b->word;b = b->next){ | |
449 pfmt(out, "%c%q", sep, b->word); | |
450 sep=' '; | |
451 } | |
452 pfmt(out, ")\n"); | |
453 } | |
454 found = 1; | |
455 } | |
456 else | |
457 found = 0; | |
458 v = gvlook(a->word); | |
459 if(v->fn) | |
460 pfmt(out, "fn %s %s\n", v->name, v->fn[v->pc-1].… | |
461 else{ | |
462 for(bp = Builtin;bp->name;bp++) | |
463 if(strcmp(a->word, bp->name)==0){ | |
464 pfmt(out, "builtin %s\n", a->wor… | |
465 break; | |
466 } | |
467 if(!bp->name){ | |
468 for(path = searchpath(a->word);path;path… | |
469 strcpy(file, path->word); | |
470 if(file[0]) | |
471 strcat(file, "/"); | |
472 strcat(file, a->word); | |
473 if(Executable(file)){ | |
474 pfmt(out, "%s\n", file); | |
475 break; | |
476 } | |
477 } | |
478 if(!path && !found){ | |
479 pfmt(err, "%s: not found\n", a->… | |
480 setstatus("not found"); | |
481 } | |
482 } | |
483 } | |
484 } | |
485 poplist(); | |
486 flush(err); | |
487 } | |
488 | |
489 void | |
490 execwait(void) | |
491 { | |
492 switch(count(runq->argv->words)){ | |
493 default: | |
494 Xerror1("Usage: wait [pid]"); | |
495 return; | |
496 case 2: | |
497 Waitfor(atoi(runq->argv->words->next->word), 0); | |
498 break; | |
499 case 1: | |
500 Waitfor(-1, 0); | |
501 break; | |
502 } | |
503 poplist(); | |
504 } |