diff options
Diffstat (limited to 'src/sash/sash.c')
-rw-r--r-- | src/sash/sash.c | 1373 |
1 files changed, 1373 insertions, 0 deletions
diff --git a/src/sash/sash.c b/src/sash/sash.c new file mode 100644 index 0000000..8c15711 --- /dev/null +++ b/src/sash/sash.c @@ -0,0 +1,1373 @@ +/* + * Copyright (c) 2014 by David I. Bell + * Permission is granted to use, distribute, or modify this source, + * provided that this copyright notice remains intact. + * + * Stand-alone shell for system maintainance for Linux. + * This program should NOT be built using shared libraries. + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <errno.h> + +#include "sash.h" + + +static const char * const version = "3.8"; + + +/* + * The special maximum argument value which means that there is + * no limit to the number of arguments on the command line. + */ +#define INFINITE_ARGS 0x7fffffff + + +/* + * One entry of the command table. + */ +typedef struct +{ + const char * name; + int (*func)(int argc, const char ** argv); + int minArgs; + int maxArgs; + const char * description; + const char * usage; +} CommandEntry; + + +/* + * The table of built-in commands. + * This is terminated wih an entry containing NULL values. + */ +static const CommandEntry commandEntryTable[] = +{ + { + "alias", do_alias, 1, INFINITE_ARGS, + "Define a command alias", + "[name [command]]" + }, + + { + "aliasall", do_aliasall, 1, 1, + "Define aliases for all of the build-in commands", + "" + }, + + { + "-ar", do_ar, 3, INFINITE_ARGS, + "Extract or list files from an AR file", + "[txp]v arFileName fileName ..." + }, + + { + "cd", do_cd, 1, 2, + "Change current directory", + "[dirName]" + }, + +#if HAVE_LINUX_ATTR + { + "-chattr", do_chattr, 3, INFINITE_ARGS, + "Change ext2 file attributes", + "[+i] [-i] [+a] [-a] fileName ..." + }, +#endif + + { + "-chgrp", do_chgrp, 3, INFINITE_ARGS, + "Change the group id of some files", + "gid fileName ..." + }, + + { + "-chmod", do_chmod, 3, INFINITE_ARGS, + "Change the protection of some files", + "mode fileName ..." + }, + + { + "-chown", do_chown, 3, INFINITE_ARGS, + "Change the owner id of some files", + "uid fileName ..." + }, + + { + "-cmp", do_cmp, 3, 3, + "Compare two files for equality", + "fileName1 fileName2" + }, + + { + "-cp", do_cp, 3, INFINITE_ARGS, + "Copy files", + "srcName ... destName" + }, + +#ifdef HAVE_LINUX_CHROOT + { + "-chroot", do_chroot, 2, 2, + "change root file system", + "new_root_dir" + }, +#endif + + { + "-dd", do_dd, 3, INFINITE_ARGS, + "Copy data between two files", + "if=name of=name [bs=n] [count=n] [skip=n] [seek=n]" + }, + + { + "-echo", do_echo, 1, INFINITE_ARGS, + "Echo the arguments", + "[args] ..." + }, + + { + "-ed", do_ed, 1, 2, + "Edit a fileName using simple line mode commands", + "[fileName]" + }, + + { + "exec", do_exec, 2, INFINITE_ARGS, + "Execute another program in place of this sash process", + "fileName [args]" + }, + + { + "exit", do_exit, 1, 2, + "Exit from sash", + "[exit value]" + }, + + { + "-file", do_file, 1, INFINITE_ARGS, + "Describe information about files", + "fileName ..." + }, + + { + "-find", do_find, 2, INFINITE_ARGS, + "Find files in a directory tree meeting some conditions", + "dirName [-xdev] [-type chars] [-name pattern] [-size minSize]" + }, + + { + "-grep", do_grep, 3, INFINITE_ARGS, + "Look for lines containing a word in some files", + "[-in] word fileName ..." + }, + +#if HAVE_GZIP + { + "-gunzip", do_gunzip, 2, INFINITE_ARGS, + "Uncompress files which were saved in GZIP or compress format", + "fileName ... [-o outputPath]" + }, + + { + "-gzip", do_gzip, 2, INFINITE_ARGS, + "Compress files into GZIP format", + "fileName ... [-o outputPath]" + }, +#endif + + { + "help", do_help, 1, 2, + "Print help about a command", + "[word]" + }, + + { + "-kill", do_kill, 2, INFINITE_ARGS, + "Send a signal to the specified process", + "[-sig] pid ..." + }, + +#ifdef HAVE_LINUX_LOSETUP + { + "-losetup", do_losetup, 3, 3, + "Associate a loopback device with a file", + "[-d] device\n -losetup device filename" + }, +#endif + + { + "-ln", do_ln, 3, INFINITE_ARGS, + "Link one fileName to another", + "[-s] srcName ... destName" + }, + + { + "-ls", do_ls, 1, INFINITE_ARGS, + "List information about files or directories", + "[-lidFC] fileName ..." + }, + +#if HAVE_LINUX_ATTR + { + "-lsattr", do_lsattr, 2, INFINITE_ARGS, + "List ext2 file attributes", + "fileName ..." + }, +#endif + + { + "-mkdir", do_mkdir, 2, INFINITE_ARGS, + "Create a directory", + "dirName ..." + }, + + { + "-mknod", do_mknod, 5, 5, + "Create a special type of file", + "fileName type major minor" + }, + + { + "-more", do_more, 2, INFINITE_ARGS, + "Type file contents page by page", + "fileName ..." + }, + + { + "-mount", do_mount, 3, INFINITE_ARGS, + "Mount or remount a filesystem on a directory", +#if HAVE_LINUX_MOUNT + "[-t type] [-r] [-s] [-e] [-m] devName dirName" +#elif HAVE_BSD_MOUNT + "[-t type] [-r] [-s] [-e] devName dirName" +#else + "[-t type] devName dirName" +#endif + }, + + { + "-mv", do_mv, 3, INFINITE_ARGS, + "Move or rename files", + "srcName ... destName" + }, + +#ifdef HAVE_LINUX_PIVOT + { + "-pivot_root", do_pivot_root, 3, 3, + "pivot the root file system", + "new_dir old_dir" + }, +#endif + + { + "-printenv", do_printenv, 1, 2, + "Print environment variables", + "[name]" + }, + + { + "prompt", do_prompt, 2, INFINITE_ARGS, + "Set the prompt string for sash", + "string" + }, + + { + "-pwd", do_pwd, 1, 1, + "Print the current working directory", + "" + }, + + { + "quit", do_exit, 1, 1, + "Exit from sash", + "" + }, + + { + "-rm", do_rm, 2, INFINITE_ARGS, + "Remove the specified files", + "fileName ..." + }, + + { + "-rmdir", do_rmdir, 2, INFINITE_ARGS, + "Remove the specified empty directories", + "dirName ..." + }, + + { + "setenv", do_setenv, 3, 3, + "Set an environment variable value", + "name value" + }, + + { + "source", do_source, 2, 2, + "Read commands from the specified file", + "fileName" + }, + + { + "-sum", do_sum, 2, INFINITE_ARGS, + "Calculate checksums of the specified files", + "fileName ..." + }, + + { + "-sync", do_sync, 1, 1, + "Sync the disks to force cached data to them", + "" + }, + + { + "-tar", do_tar, 2, INFINITE_ARGS, + "Create, extract, or list files from a TAR file", + "[cxtv]f tarFileName fileName ..." + }, + + { + "-touch", do_touch, 2, INFINITE_ARGS, + "Update times or create the specified files", + "fileName ..." + }, + + { + "umask", do_umask, 1, 2, + "Set the umask value for file protections", + "[mask]" + }, + + { +#if HAVE_BSD_MOUNT + "-umount", do_umount, 2, 3, + "Unmount a filesystem", + "[-f] fileName" +#else + "-umount", do_umount, 2, 2, + "Unmount a filesystem", + "fileName" +#endif + }, + + { + "unalias", do_unalias, 2, 2, + "Remove a command alias", + "name" + }, + + { + "-where", do_where, 2, 2, + "Type the location of a program", + "program" + }, + + { + NULL, 0, 0, 0, + NULL, + NULL + } +}; + + +/* + * The definition of an command alias. + */ +typedef struct +{ + char * name; + char * value; +} Alias; + + +/* + * Local data. + */ +static Alias * aliasTable; +static int aliasCount; + +static FILE * sourcefiles[MAX_SOURCE]; +static int sourceCount; + +static BOOL intCrlf = TRUE; +static char * prompt; + + +/* + * Local procedures. + */ +static void catchInt(int); +static void catchQuit(int); +static int readFile(const char * name); +static int command(const char * cmd); +static BOOL tryBuiltIn(const char * cmd); +static int runCmd(const char * cmd); +static void childProcess(const char * cmd); +static void showPrompt(void); +static void usage(void); +static Alias * findAlias(const char * name); +static void expandVariable(char * name); + + +/* + * Global interrupt flag. + */ +BOOL intFlag; + + +int +main(int argc, const char ** argv) +{ + const char * cp; + const char * singleCommand; + const char * commandFile; + BOOL quietFlag; + BOOL aliasFlag; + BOOL interactiveFlag; + char buf[PATH_LEN]; + + singleCommand = NULL; + commandFile = NULL; + quietFlag = FALSE; + aliasFlag = FALSE; + interactiveFlag = FALSE; + + /* + * Look for options. + */ + argv++; + argc--; + + while ((argc > 0) && (**argv == '-')) + { + cp = *argv++ + 1; + argc--; + + while (*cp) switch (*cp++) + { + case '-': + /* + * Ignore. This is so that we can be + * run from login. + */ + break; + + case 'c': + /* + * Execute specified command. + */ + if ((argc != 1) || singleCommand || interactiveFlag) + usage(); + + singleCommand = *argv++; + argc--; + + break; + + case 'f': + /* + * Execute commands from file. + * This is used for sash script files. + * The quiet flag is also set. + */ + if ((argc != 1) || commandFile) + usage(); + + quietFlag = TRUE; + commandFile = *argv++; + argc--; + + break; + + case 'i': + /* + * Be an interactive shell + * ..is a no-op, but some contexts require this + * ..interactiveFlag is to avoid -ic as a legacy + */ + if (singleCommand) + usage(); + + interactiveFlag = TRUE; + break; + + case 'p': + /* + * Set the prompt string. + */ + if ((argc <= 0) || (**argv == '-')) + usage(); + + if (prompt) + free(prompt); + + prompt = strdup(*argv++); + argc--; + + break; + + case 'q': + quietFlag = TRUE; + break; + + case 'a': + aliasFlag = TRUE; + break; + + case 'h': + case '?': + usage(); + break; + + default: + fprintf(stderr, "Unknown option -%c\n", cp[-1]); + + return 1; + } + } + + /* + * No more arguments are allowed. + */ + if (argc > 0) + usage(); + + /* + * Default our path if it is not set. + */ + if (getenv("PATH") == NULL) + putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc"); + + /* + * If the alias flag is set then define all aliases. + */ + if (aliasFlag) + do_aliasall(0, NULL); + + /* + * If we are to execute a single command, then do so and exit. + */ + if (singleCommand) + { + return command(singleCommand); + } + + /* + * Print a hello message unless we are told to be silent. + */ + if (!quietFlag && isatty(STDIN)) + { + printf("Stand-alone shell (version %s)\n", version); + + if (aliasFlag) + printf("Built-in commands are aliased to standard commands\n"); + } + + signal(SIGINT, catchInt); + signal(SIGQUIT, catchQuit); + + /* + * Execute the user's alias file if present. + */ + cp = getenv("HOME"); + + if (cp) + { + strcpy(buf, cp); + strcat(buf, "/"); + strcat(buf, ".aliasrc"); + + if ((access(buf, 0) == 0) || (errno != ENOENT)) + readFile(buf); + } + + /* + * Read commands from stdin or from a command file. + */ + return readFile(commandFile); + +} + + +/* + * Read commands from the specified file. + * A null name pointer indicates to read from stdin. + */ +static int +readFile(const char * name) +{ + FILE * fp; + int cc; + BOOL ttyFlag; + char buf[CMD_LEN]; + int r = 0; + + if (sourceCount >= MAX_SOURCE) + { + fprintf(stderr, "Too many source files\n"); + + return 1; + } + + fp = stdin; + + if (name) + { + fp = fopen(name, "r"); + + if (fp == NULL) + { + perror(name); + + return 1; + } + } + + sourcefiles[sourceCount++] = fp; + + ttyFlag = isatty(fileno(fp)); + + while (TRUE) + { + if (ttyFlag) + showPrompt(); + + if (intFlag && !ttyFlag && (fp != stdin)) + { + fclose(fp); + sourceCount--; + + return 1; + } + + if (fgets(buf, CMD_LEN - 1, fp) == NULL) + { + if (ferror(fp) && (errno == EINTR)) + { + clearerr(fp); + + continue; + } + + break; + } + + cc = strlen(buf); + + if (buf[cc - 1] == '\n') + cc--; + + while ((cc > 0) && isBlank(buf[cc - 1])) + cc--; + + buf[cc] = '\0'; + + r = command(buf); + } + + if (ferror(fp)) + { + perror("Reading command line"); + + if (fp == stdin) + exit(1); + } + + clearerr(fp); + + if (fp != stdin) + fclose(fp); + + sourceCount--; + + return r; +} + + +/* + * Parse and execute one null-terminated command line string. + * This breaks the command line up into words, checks to see if the + * command is an alias, and expands wildcards. + */ +static int +command(const char * cmd) +{ + const char * endCmd; + const Alias * alias; + char newCommand[CMD_LEN]; + char cmdName[CMD_LEN]; + + /* + * Rest the interrupt flag and free any memory chunks that + * were allocated by the previous command. + */ + intFlag = FALSE; + + freeChunks(); + + /* + * Skip leading blanks. + */ + while (isBlank(*cmd)) + cmd++; + + /* + * If the command is empty or is a comment then ignore it. + */ + if ((*cmd == '\0') || (*cmd == '#')) + return 0; + + /* + * Look for the end of the command name and then copy the + * command name to a buffer so we can null terminate it. + */ + endCmd = cmd; + + while (*endCmd && !isBlank(*endCmd)) + endCmd++; + + memcpy(cmdName, cmd, endCmd - cmd); + + cmdName[endCmd - cmd] = '\0'; + + /* + * Search for the command name in the alias table. + * If it is found, then replace the command name with + * the alias value, and append the current command + * line arguments to that. + */ + alias = findAlias(cmdName); + + if (alias) + { + strcpy(newCommand, alias->value); + strcat(newCommand, endCmd); + + cmd = newCommand; + } + + /* + * Expand simple environment variables + */ + while (strstr(cmd, "$(")) expandVariable((char *)cmd); + + /* + * Now look for the command in the builtin table, and execute + * the command if found. + */ + if (tryBuiltIn(cmd)) + return 0; /* This is a blatant lie */ + + /* + * The command is not a built-in, so run the program along + * the PATH list. + */ + return runCmd(cmd); +} + + +/* + * Try to execute a built-in command. + * Returns TRUE if the command is a built in, whether or not the + * command succeeds. Returns FALSE if this is not a built-in command. + */ +static BOOL +tryBuiltIn(const char * cmd) +{ + const char * endCmd; + const CommandEntry * entry; + int argc; + const char ** argv; + char cmdName[CMD_LEN]; + + /* + * Look for the end of the command name and then copy the + * command name to a buffer so we can null terminate it. + */ + endCmd = cmd; + + while (*endCmd && !isBlank(*endCmd)) + endCmd++; + + memcpy(cmdName, cmd, endCmd - cmd); + + cmdName[endCmd - cmd] = '\0'; + + /* + * Search the command table looking for the command name. + */ + for (entry = commandEntryTable; entry->name != NULL; entry++) + { + if (strcmp(entry->name, cmdName) == 0) + break; + } + + /* + * If the command is not a built-in, return indicating that. + */ + if (entry->name == NULL) + return FALSE; + + /* + * The command is a built-in. + * Break the command up into arguments and expand wildcards. + */ + if (!makeArgs(cmd, &argc, &argv)) + return TRUE; + + /* + * Give a usage string if the number of arguments is too large + * or too small. + */ + if ((argc < entry->minArgs) || (argc > entry->maxArgs)) + { + fprintf(stderr, "usage: %s %s\n", entry->name, entry->usage); + + return TRUE; + } + + /* + * Call the built-in function with the argument list. + */ + entry->func(argc, argv); + + return TRUE; +} + + +/* + * Execute the specified command either by forking and executing + * the program ourself, or else by using the shell. Returns the + * exit status, or -1 if the program cannot be executed at all. + */ +static int +runCmd(const char * cmd) +{ + const char * cp; + BOOL magic; + pid_t pid; + int status; + + /* + * Check the command for any magic shell characters + * except for quoting. + */ + magic = FALSE; + + for (cp = cmd; *cp; cp++) + { + if ((*cp >= 'a') && (*cp <= 'z')) + continue; + + if ((*cp >= 'A') && (*cp <= 'Z')) + continue; + + if (isDecimal(*cp)) + continue; + + if (isBlank(*cp)) + continue; + + if ((*cp == '.') || (*cp == '/') || (*cp == '-') || + (*cp == '+') || (*cp == '=') || (*cp == '_') || + (*cp == ':') || (*cp == ',') || (*cp == '\'') || + (*cp == '"')) + { + continue; + } + + magic = TRUE; + } + + /* + * If there were any magic characters used then run the + * command using the shell. + */ + if (magic) + return trySystem(cmd); + + /* + * No magic characters were in the command, so we can do the fork + * and exec ourself. + */ + pid = fork(); + + if (pid < 0) + { + perror("fork failed"); + + return -1; + } + + /* + * If we are the child process, then go execute the program. + */ + if (pid == 0) + childProcess(cmd); + + /* + * We are the parent process. + * Wait for the child to complete. + */ + status = 0; + intCrlf = FALSE; + + while (((pid = waitpid(pid, &status, 0)) < 0) && (errno == EINTR)) + ; + + intCrlf = TRUE; + + if (pid < 0) + { + fprintf(stderr, "Error from waitpid: %s", strerror(errno)); + + return -1; + } + + if (WIFSIGNALED(status)) + { + fprintf(stderr, "pid %ld: killed by signal %d\n", + (long) pid, WTERMSIG(status)); + + return -1; + } + + return WEXITSTATUS(status); +} + + +/* + * Here as the child process to try to execute the command. + * This is only called if there are no meta-characters in the command. + * This procedure never returns. + */ +static void +childProcess(const char * cmd) +{ + const char ** argv; + int argc; + + /* + * Close any extra file descriptors we have opened. + */ + while (--sourceCount >= 0) + { + if (sourcefiles[sourceCount] != stdin) + fclose(sourcefiles[sourceCount]); + } + + /* + * Break the command line up into individual arguments. + * If this fails, then run the shell to execute the command. + */ + if (!makeArgs(cmd, &argc, &argv)) + { + int status = trySystem(cmd); + + if (status == -1) + exit(99); + + exit(status); + } + + /* + * Try to execute the program directly. + */ + execvp(argv[0], (char **) argv); + + /* + * The exec failed, so try to run the command using the shell + * in case it is a shell script. + */ + if (errno == ENOEXEC) + { + int status = trySystem(cmd); + + if (status == -1) + exit(99); + + exit(status); + } + + /* + * There was something else wrong, complain and exit. + */ + perror(argv[0]); + exit(1); +} + + +int +do_help(int argc, const char ** argv) +{ + const CommandEntry * entry; + const char * str; + + str = NULL; + + if (argc == 2) + str = argv[1]; + + /* + * Check for an exact match, in which case describe the program. + */ + if (str) + { + for (entry = commandEntryTable; entry->name; entry++) + { + if (strcmp(str, entry->name) == 0) + { + printf("%s\n", entry->description); + + printf("usage: %s %s\n", entry->name, + entry->usage); + + return 0; + } + } + } + + /* + * Print short information about commands which contain the + * specified word. + */ + for (entry = commandEntryTable; entry->name; entry++) + { + if ((str == NULL) || (strstr(entry->name, str) != NULL) || + (strstr(entry->usage, str) != NULL)) + { + printf("%-10s %s\n", entry->name, entry->usage); + } + } + + return 0; +} + + +int +do_alias(int argc, const char ** argv) +{ + const char * name; + char * value; + Alias * alias; + int count; + char buf[CMD_LEN]; + + if (argc < 2) + { + count = aliasCount; + + for (alias = aliasTable; count-- > 0; alias++) + printf("%s\t%s\n", alias->name, alias->value); + + return 0; + } + + name = argv[1]; + + if (argc == 2) + { + alias = findAlias(name); + + if (alias) + printf("%s\n", alias->value); + else + { + fprintf(stderr, "Alias \"%s\" is not defined\n", name); + + return 1; + } + + return 0; + } + + if (strcmp(name, "alias") == 0) + { + fprintf(stderr, "Cannot alias \"alias\"\n"); + + return 1; + } + + if (!makeString(argc - 2, argv + 2, buf, CMD_LEN)) + return 1; + + value = malloc(strlen(buf) + 1); + + if (value == NULL) + { + fprintf(stderr, "No memory for alias value\n"); + + return 1; + } + + strcpy(value, buf); + + alias = findAlias(name); + + if (alias) + { + free(alias->value); + alias->value = value; + + return 0; + } + + if ((aliasCount % ALIAS_ALLOC) == 0) + { + count = aliasCount + ALIAS_ALLOC; + + if (aliasTable) + { + alias = (Alias *) realloc(aliasTable, + sizeof(Alias) * count); + } + else + alias = (Alias *) malloc(sizeof(Alias) * count); + + if (alias == NULL) + { + free(value); + fprintf(stderr, "No memory for alias table\n"); + + return 1; + } + + aliasTable = alias; + } + + alias = &aliasTable[aliasCount]; + + alias->name = malloc(strlen(name) + 1); + + if (alias->name == NULL) + { + free(value); + fprintf(stderr, "No memory for alias name\n"); + + return 1; + } + + strcpy(alias->name, name); + alias->value = value; + aliasCount++; + + return 0; +} + + +/* + * Build aliases for all of the built-in commands which start with a dash, + * using the names without the dash. + */ +int +do_aliasall(int argc, const char **argv) +{ + const CommandEntry * entry; + const char * name; + const char * newArgv[4]; + + for (entry = commandEntryTable; entry->name; entry++) + { + name = entry->name; + + if (*name != '-') + continue; + + newArgv[0] = "alias"; + newArgv[1] = name + 1; + newArgv[2] = name; + newArgv[3] = NULL; + + do_alias(3, newArgv); + } + + return 0; +} + + +/* + * Look up an alias name, and return a pointer to it. + * Returns NULL if the name does not exist. + */ +static Alias * +findAlias(const char * name) +{ + Alias * alias; + int count; + + count = aliasCount; + + for (alias = aliasTable; count-- > 0; alias++) + { + if (strcmp(name, alias->name) == 0) + return alias; + } + + return NULL; +} + + +int +do_source(int argc, const char ** argv) +{ + return readFile(argv[1]); +} + + +int +do_exec(int argc, const char ** argv) +{ + const char * name; + + name = argv[1]; + + while (--sourceCount >= 0) + { + if (sourcefiles[sourceCount] != stdin) + fclose(sourcefiles[sourceCount]); + } + + argv[argc] = NULL; + + execvp(name, (char **) argv + 1); + perror(name); + + return 1; +} + + +int +do_prompt(int argc, const char ** argv) +{ + char * cp; + char buf[CMD_LEN]; + + if (!makeString(argc - 1, argv + 1, buf, CMD_LEN)) + return 1; + + cp = malloc(strlen(buf) + 2); + + if (cp == NULL) + { + fprintf(stderr, "No memory for prompt\n"); + + return 1; + } + + strcpy(cp, buf); + strcat(cp, " "); + + if (prompt) + free(prompt); + + prompt = cp; + + return 0; +} + + +int +do_unalias(int argc, const char ** argv) +{ + Alias * alias; + + while (--argc > 0) + { + alias = findAlias(*++argv); + + if (alias == NULL) + continue; + + free(alias->name); + free(alias->value); + aliasCount--; + alias->name = aliasTable[aliasCount].name; + alias->value = aliasTable[aliasCount].value; + } + + return 0; +} + + +/* + * Display the prompt string. + */ +static void +showPrompt(void) +{ + const char * cp; + + cp = "> "; + + if (prompt) + cp = prompt; + + tryWrite(STDOUT, cp, strlen(cp)); +} + + +static void +catchInt(int val) +{ + signal(SIGINT, catchInt); + + intFlag = TRUE; + + if (intCrlf) + tryWrite(STDOUT, "\n", 1); +} + + +static void +catchQuit(int val) +{ + signal(SIGQUIT, catchQuit); + + intFlag = TRUE; + + if (intCrlf) + tryWrite(STDOUT, "\n", 1); +} + + +/* + * Print the usage information and quit. + */ +static void +usage(void) +{ + fprintf(stderr, "Stand-alone shell (version %s)\n", version); + fprintf(stderr, "\n"); + fprintf(stderr, "Usage: sash [-a] [-q] [-f fileName] [-c command] [-p prompt] [-i]\n"); + + exit(1); +} + + +/* + * Expand one environment variable: Syntax $(VAR) + */ +static void +expandVariable(char * cmd) +{ + char tmp[CMD_LEN]; + char *cp; + char *ep; + + strcpy(tmp, cmd); + cp = strstr(tmp, "$("); + if (cp) { + *cp++ = '\0'; + strcpy(cmd, tmp); + ep = ++cp; + while (*ep && (*ep != ')')) ep++; + if (*ep == ')') *ep++ = '\0'; + cp = getenv(cp); + if (cp) strcat(cmd, cp); + strcat(cmd, ep); + } + return; +} + +/* END CODE */ |