libgpg-error: fix riscv64
[openadk.git] / package / sash / src / sash.c
blobf62839de6d357c9434e3c0d56b46e7151ab55b86
1 /*
2 * Copyright (c) 1993 by David I. Bell
3 * Permission is granted to use, distribute, or modify this source,
4 * provided that this copyright notice remains intact.
6 * Stand-alone shell for system maintainance for Linux.
7 * This program should NOT be built using shared libraries.
9 * 1.1.1, hacked to re-allow cmd line invocation of script file
10 * Pat Adamo, padamo@unix.asb.com
13 #include "sash.h"
15 #include <stdlib.h>
16 #include <signal.h>
17 #include <errno.h>
18 #include <unistd.h>
19 #include <sys/stat.h>
20 #include <sys/time.h>
21 #include <sys/wait.h>
23 static const char enoent_msg[] = "Bad command or file name";
24 static const char unkerr_msg[] = "Unknown error!";
26 extern int intflag;
28 extern void do_test();
30 typedef struct {
31 char name[10];
32 char usage[30];
33 void (*func)();
34 int minargs;
35 int maxargs;
36 } CMDTAB;
39 CMDTAB cmdtab[] = {
40 "cat", "filename ...",
41 do_cat, 2, MAXARGS,
43 "cd", "[dirname]",
44 do_cd, 1, 2,
46 "chgrp", "gid filename ...",
47 do_chgrp, 3, MAXARGS,
49 "chmod", "mode filename ...",
50 do_chmod, 3, MAXARGS,
52 "chown", "uid filename ...",
53 do_chown, 3, MAXARGS,
55 "cmp", "filename1 filename2",
56 do_cmp, 3, 3,
58 "cp", "srcname ... destname",
59 do_cp, 3, MAXARGS,
61 "date", "date [MMDDhhmm[YYYY]]",
62 do_date, 1, 2,
64 "df", "[file-system]",
65 do_df, 1, 2,
67 "echo", "[args] ...",
68 do_echo, 1, MAXARGS,
70 "exec", "filename [args]",
71 do_exec, 2, MAXARGS,
73 "exit", "",
74 do_exit, 1, 1,
76 "free", "",
77 do_free, 1, 1,
79 "help", "",
80 do_help, 1, MAXARGS,
82 "hexdump", "[-s pos] filename",
83 do_hexdump, 1, 4,
85 "hostname", "[hostname]",
86 do_hostname, 1, 2,
88 "kill", "[-sig] pid ...",
89 do_kill, 2, MAXARGS,
91 "ln", "[-s] srcname ... destname",
92 do_ln, 3, MAXARGS,
94 "ls", "[-lidC] filename ...",
95 do_ls, 1, MAXARGS,
97 "mkdir", "dirname ...",
98 do_mkdir, 2, MAXARGS,
100 "mknod", "filename type major minor",
101 do_mknod, 5, 5,
103 "more", "filename ...",
104 do_more, 2, MAXARGS,
106 "mount", "[-t type] devname dirname",
107 do_mount, 3, MAXARGS,
109 "mv", "srcname ... destname",
110 do_mv, 3, MAXARGS,
112 "pid", "",
113 do_pid, 1, 1,
115 "printenv", "[name]",
116 do_printenv, 1, 2,
118 "ps", "",
119 do_ps, 1, MAXARGS,
121 "pwd", "",
122 do_pwd, 1, 1,
124 "quit", "",
125 do_exit, 1, 1,
127 "rm", "filename ...",
128 do_rm, 2, MAXARGS,
130 "rmdir", "dirname ...",
131 do_rmdir, 2, MAXARGS,
133 "setenv", "name value",
134 do_setenv, 3, 3,
136 "sleep", "seconds",
137 do_sleep, 1, 2,
139 "source", "filename",
140 do_source, 2, 2,
142 "sync", "",
143 do_sync, 1, 1,
145 "touch", "filename ...",
146 do_touch, 2, MAXARGS,
148 "umask", "[mask]",
149 do_umask, 1, 2,
151 "umount", "filename",
152 do_umount, 2, 2,
154 0, 0, 0,
155 0, 0
159 typedef struct {
160 char *name;
161 char *value;
162 } ALIAS;
165 static ALIAS *aliastable;
166 static int aliascount;
168 static FILE *sourcefiles[MAXSOURCE];
169 static int sourcecount;
171 volatile static BOOL intcrlf = TRUE;
174 static void catchint();
175 static void catchquit();
176 static void catchchild();
177 static void readfile();
178 static void command();
179 static void runcmd();
180 static void showprompt();
181 static BOOL trybuiltin();
182 static BOOL command_in_path();
183 static ALIAS *findalias();
185 extern char ** environ;
187 char buf[CMDLEN];
188 int exit_code = 0;
190 int main(argc, argv, env)
191 int argc;
192 char **argv;
193 char *env[];
195 struct sigaction act;
196 char *cp;
197 int dofile = 0;
199 if ((argc > 1) && !strcmp(argv[1], "-c")) {
200 /* We are that fancy a shell */
201 buf[0] = '\0';
202 for (dofile = 2; dofile < argc; dofile++) {
203 strncat(buf, argv[dofile], sizeof(buf));
204 if (dofile + 1 < argc)
205 strncat(buf, " ", sizeof(buf));
207 command(buf, FALSE);
208 exit(exit_code);
211 if ((argc > 1) && strcmp(argv[1], "-t"))
213 dofile++;
214 printf("Shell invoked to run file: %s\n",argv[1]);
216 else
217 printf("\nSash command shell (OpenADK edition)\n");
218 fflush(stdout);
220 signal(SIGINT, catchint);
221 signal(SIGQUIT, catchquit);
223 memset(&act, 0, sizeof(act));
224 act.sa_handler = catchchild;
225 act.sa_flags = SA_RESTART;
226 sigaction(SIGCHLD, &act, NULL);
228 if (getenv("PATH") == NULL)
229 putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin");
231 readfile(dofile ? argv[1] : NULL);
232 exit(exit_code);
237 * Read commands from the specified file.
238 * A null name pointer indicates to read from stdin.
240 static void
241 readfile(name)
242 char *name;
244 FILE *fp;
245 int cc;
246 BOOL ttyflag;
247 char *ptr;
249 if (sourcecount >= MAXSOURCE) {
250 fprintf(stderr, "Too many source files\n");
251 return;
254 fp = stdin;
255 if (name) {
256 fp = fopen(name, "r");
257 if (fp == NULL) {
258 perror(name);
259 return;
262 sourcefiles[sourcecount++] = fp;
264 ttyflag = isatty(fileno(fp));
266 while (TRUE) {
267 fflush(stdout);
268 if (fp == stdin) //using terminal, so show prompt
269 showprompt();
271 if (intflag && !ttyflag && (fp != stdin)) {
272 fclose(fp);
273 sourcecount--;
274 return;
277 if (fgets(buf, CMDLEN - 1, fp) == NULL) {
278 if (ferror(fp) && (errno == EINTR)) {
279 clearerr(fp);
280 continue;
282 break;
285 cc = strlen(buf);
287 while ((cc > 0) && isspace(buf[cc - 1]))
288 cc--;
289 buf[cc] = '\0';
290 /* remove leading spaces and look for a '#' */
291 ptr = &buf[0];
292 while (*ptr == ' ') {
293 ptr++;
295 if (*ptr != '#') {
296 if (fp != stdin) {
297 //taking commands from file - echo
298 printf("Command: %s\n",buf);
299 } //end if (fp != stdin)
301 command(buf, fp == stdin);
307 if (ferror(fp)) {
308 perror("Reading command line");
309 if (fp == stdin)
310 exit(1);
313 clearerr(fp);
314 if (fp != stdin) {
315 fclose(fp);
316 printf("Execution Finished, Exiting\n");
319 sourcecount--;
324 * Parse and execute one null-terminated command line string.
325 * This breaks the command line up into words, checks to see if the
326 * command is an alias, and expands wildcards.
328 static void
329 command(cmd, do_history)
330 int do_history;
331 char *cmd;
333 ALIAS *alias;
334 char **argv;
335 int argc;
336 int bg;
337 char *c;
339 char last_exit_code[10];
341 sprintf(last_exit_code, "%d", exit_code);
343 intflag = FALSE;
344 exit_code = 0;
346 freechunks();
348 while (isblank(*cmd))
349 cmd++;
351 if (do_history) {
352 int i;
353 static char *history[HISTORY_SIZE];
355 if (*cmd == '!') {
356 if (cmd[1] == '!')
357 i = 0;
358 else {
359 i = atoi(cmd+1) - 1;
360 if (i < 0 || i >= HISTORY_SIZE) {
361 printf("%s: Out of range\n", cmd);
362 return;
365 if (history[i] == NULL) {
366 printf("%s: Null entry\n", cmd);
367 return;
369 strcpy(cmd, history[i]);
370 } else if (*cmd == 'h' && cmd[1] == '\0') {
371 for (i=0; i<HISTORY_SIZE; i++) {
372 if (history[i] != NULL)
373 printf("%2d: %s\n", i+1, history[i]);
375 return;
376 } else if (*cmd != '\0') {
377 if (history[HISTORY_SIZE-1] != NULL)
378 free(history[HISTORY_SIZE-1]);
379 for (i=HISTORY_SIZE-1; i>0; i--)
380 history[i] = history[i-1];
381 history[0] = strdup(cmd);
384 if (c = strchr(cmd, '&')) {
385 *c = '\0';
386 bg = 1;
387 } else
388 bg = 0;
390 /* Set the last exit code */
391 setenv("?", last_exit_code, 1);
393 if ((cmd = expandenvvar(cmd)) == NULL)
394 return;
396 if ((*cmd == '\0') || !makeargs(cmd, &argc, &argv))
397 return;
400 * Search for the command in the alias table.
401 * If it is found, then replace the command name with
402 * the alias, and append any other arguments to it.
404 alias = findalias(argv[0]);
405 if (alias) {
406 cmd = buf;
407 strcpy(cmd, alias->value);
409 while (--argc > 0) {
410 strcat(cmd, " ");
411 strcat(cmd, *++argv);
414 if (!makeargs(cmd, &argc, &argv))
415 return;
419 * BASH-style variable setting
421 if (argc == 1) {
422 c = index(argv[0], '=');
423 if (c > argv[0]) {
424 *c++ = '\0';
425 setenv(argv[0], c, 1);
426 return;
431 * Now look for the command in the builtin table, and execute
432 * the command if found.
434 if (!strcmp(argv[0], "builtin")) {
435 --argc;
436 ++argv;
437 if (!*argv || !trybuiltin(argc, argv))
438 fprintf(stderr, "%s: %s\n", argv[-1], enoent_msg);
439 return;
440 } else if (!command_in_path(argv[0]) && trybuiltin(argc, argv))
441 return;
444 * Not found, run the program along the PATH list.
446 runcmd(cmd, bg, argc, argv);
451 * return true if we find this command in our
452 * path.
454 static BOOL
455 command_in_path(char *cmd)
457 struct stat stat_buf;
459 if (strchr(cmd, '/') == 0) {
460 char * path;
461 static char path_copy[PATHLEN];
463 /* Search path for binary */
464 for (path = getenv("PATH"); path && *path; ) {
465 char * p2;
467 strcpy(path_copy, path);
468 if (p2 = strchr(path_copy, ':')) {
469 *p2 = '\0';
472 if (strlen(path_copy))
473 strcat(path_copy, "/");
474 strcat(path_copy, cmd);
476 if (!stat(path_copy, &stat_buf) && (stat_buf.st_mode & 0111))
477 return(TRUE);
479 p2 = strchr(path, ':');
480 if (p2)
481 path = p2 + 1;
482 else
483 path = 0;
485 } else if (!stat(cmd, &stat_buf) && (stat_buf.st_mode & 0111))
486 return(TRUE);
487 return(FALSE);
492 * Try to execute a built-in command.
493 * Returns TRUE if the command is a built in, whether or not the
494 * command succeeds. Returns FALSE if this is not a built-in command.
496 static BOOL
497 trybuiltin(argc, argv)
498 int argc;
499 char **argv;
501 CMDTAB *cmdptr;
502 int oac;
503 int newargc;
504 int matches;
505 int i;
506 char *newargv[MAXARGS];
507 char *nametable[MAXARGS];
509 cmdptr = cmdtab - 1;
510 do {
511 cmdptr++;
512 if (cmdptr->name[0] == 0)
513 return FALSE;
515 } while (strcmp(argv[0], cmdptr->name));
518 * Give a usage string if the number of arguments is too large
519 * or too small.
521 if ((argc < cmdptr->minargs) || (argc > cmdptr->maxargs)) {
522 fprintf(stderr, "usage: %s %s\n",
523 cmdptr->name, cmdptr->usage);
524 fflush(stderr);
526 return TRUE;
530 * Now for each command argument, see if it is a wildcard, and if
531 * so, replace the argument with the list of matching filenames.
533 newargv[0] = argv[0];
534 newargc = 1;
535 oac = 0;
537 while (++oac < argc) {
538 if (argv[oac][0] == '"' || argv[oac][0] == '\'') {
539 argv[oac]++;
540 matches = 0;
542 else {
543 matches = expandwildcards(argv[oac], MAXARGS, nametable);
544 if (matches < 0)
545 return TRUE;
548 if ((newargc + matches) >= MAXARGS) {
549 fprintf(stderr, "Too many arguments\n");
550 return TRUE;
553 if (matches == 0)
554 newargv[newargc++] = argv[oac];
556 for (i = 0; i < matches; i++)
557 newargv[newargc++] = nametable[i];
560 (*cmdptr->func)(newargc, newargv);
562 return TRUE;
566 * Execute the specified command.
568 static void
569 runcmd(cmd, bg, argc, argv)
570 char *cmd;
571 int bg;
572 int argc;
573 char **argv;
575 register char * cp;
576 int pid;
577 int status;
578 int oac;
579 int newargc;
580 int matches;
581 int i;
582 char *newargv[MAXARGS];
583 char *nametable[MAXARGS];
584 struct sigaction act;
586 newargv[0] = argv[0];
589 * Now for each command argument, see if it is a wildcard, and if
590 * so, replace the argument with the list of matching filenames.
592 newargc = 1;
593 oac = 0;
595 while (++oac < argc) {
596 if (argv[oac][0] == '"' || argv[oac][0] == '\'') {
597 argv[oac]++;
598 matches = 0;
600 else {
601 matches = expandwildcards(argv[oac], MAXARGS, nametable);
602 if (matches < 0)
603 return;
606 if ((newargc + matches) >= MAXARGS) {
607 fprintf(stderr, "Too many arguments\n");
608 return;
611 if (matches == 0)
612 newargv[newargc++] = argv[oac];
614 for (i = 0; i < matches; i++)
615 newargv[newargc++] = nametable[i];
618 newargv[newargc] = 0;
620 if (!bg)
621 signal(SIGCHLD, SIG_DFL);
624 * Do the fork and exec ourselves.
625 * If this fails with ENOEXEC, then run the
626 * shell anyway since it might be a shell script.
628 if (!(pid = vfork())) {
629 int ci;
630 char errbuf[50];
633 * We are the child, so run the program.
634 * First close any extra file descriptors we have opened.
635 * be sure not to modify any globals after the vfork !
638 for (ci = 0; ci < sourcecount; ci++)
639 if (sourcefiles[ci] != stdin)
640 close(fileno(sourcefiles[ci]));
642 signal(SIGINT, SIG_DFL);
643 signal(SIGQUIT, SIG_DFL);
644 signal(SIGCHLD, SIG_DFL);
646 execvp(newargv[0], newargv);
648 ci = errno;
649 write(2, newargv[0], strlen(newargv[0]));
650 write(2, ": ", 2);
651 if (ci == ENOENT)
652 write(2, enoent_msg, sizeof(enoent_msg) - 1);
653 else if (strerror_r(ci, errbuf, sizeof(errbuf)))
654 write(2, unkerr_msg, sizeof(unkerr_msg) - 1);
655 else
656 write(2, errbuf, strlen(errbuf));
657 write(2, "\n", 1);
659 _exit(ci == ENOENT ? 127 : 126);
662 if (pid < 0) {
663 memset(&act, 0, sizeof(act));
664 act.sa_handler = catchchild;
665 act.sa_flags = SA_RESTART;
666 sigaction(SIGCHLD, &act, NULL);
668 perror("vfork failed");
669 return;
672 if (bg) {
673 printf("[%d]\n", pid);
674 return;
677 if (pid) {
678 int cpid;
679 status = 0;
680 intcrlf = FALSE;
682 for (;;) {
683 cpid = wait4(pid, &status, 0, 0);
684 if ((cpid < 0) && (errno == EINTR))
685 continue;
686 if (cpid < 0)
687 break;
688 if (cpid != pid) {
689 fprintf(stderr, "sh %d: child %d died\n", getpid(), cpid);
690 continue;
694 act.sa_handler = catchchild;
695 memset(&act.sa_mask, 0, sizeof(act.sa_mask));
696 act.sa_flags = SA_RESTART;
697 sigaction(SIGCHLD, &act, NULL);
699 intcrlf = TRUE;
701 if (WIFEXITED(status)) {
702 if (WEXITSTATUS(status) == 0)
703 return;
704 exit_code = WEXITSTATUS(status);
705 } else
706 exit_code = 1;
708 return;
711 perror(argv[0]);
712 exit(1);
715 void
716 do_help(argc, argv)
717 int argc;
718 char **argv;
720 CMDTAB *cmdptr;
722 for (cmdptr = cmdtab; cmdptr->name && cmdptr->name[0]; cmdptr++)
723 printf("%-10s %s\n", cmdptr->name, cmdptr->usage);
727 * Look up an alias name, and return a pointer to it.
728 * Returns NULL if the name does not exist.
730 static ALIAS *
731 findalias(name)
732 char *name;
734 ALIAS *alias;
735 int count;
737 count = aliascount;
738 for (alias = aliastable; count-- > 0; alias++) {
739 if (strcmp(name, alias->name) == 0)
740 return alias;
743 return NULL;
747 void
748 do_source(argc, argv)
749 int argc;
750 char **argv;
752 readfile(argv[1]);
755 void
756 do_pid(argc, argv)
757 int argc;
758 char **argv;
760 printf("%d\n", getpid());
763 void
764 do_exec(argc, argv)
765 int argc;
766 char **argv;
768 while (--sourcecount >= 0) {
769 if (sourcefiles[sourcecount] != stdin)
770 fclose(sourcefiles[sourcecount]);
773 argv[argc] = NULL;
774 execvp(argv[1], &argv[1]);
776 perror(argv[1]);
777 exit(1);
781 * Display the prompt string.
783 static void
784 showprompt()
786 char *cp;
787 char buf[60];
789 if ((cp = getenv("PS1")) != NULL) {
790 printf("%s", cp);
792 else {
793 *buf = '\0';
794 getcwd(buf, sizeof(buf) - 1);
795 printf("%s> ", buf);
797 fflush(stdout);
801 static void
802 catchint()
804 signal(SIGINT, catchint);
806 intflag = TRUE;
808 if (intcrlf)
809 write(STDOUT, "\n", 1);
813 static void
814 catchquit()
816 signal(SIGQUIT, catchquit);
818 intflag = TRUE;
820 if (intcrlf)
821 write(STDOUT, "\n", 1);
824 static void
825 catchchild()
827 char buf[40];
828 pid_t pid;
829 int status;
831 pid = wait4(-1, &status, WUNTRACED, 0);
832 if (WIFSTOPPED(status))
833 sprintf(buf, "sh %d: Child %d stopped\n", getpid(), pid);
834 else
835 sprintf(buf, "sh %d: Child %d died\n", getpid(), pid);
837 if (intcrlf)
838 write(STDOUT, "\n", 1);
840 write(STDOUT, buf, strlen(buf));
843 /* END CODE */