diff options
-rw-r--r-- | Makefile | 10 | ||||
-rw-r--r-- | interactive.c | 36 | ||||
-rw-r--r-- | set.c | 5 | ||||
-rw-r--r-- | sh.l | 9 | ||||
-rw-r--r-- | sh.y | 8 | ||||
-rw-r--r-- | shed.c | 65 | ||||
-rw-r--r-- | shed.h | 92 | ||||
-rw-r--r-- | shed_commands.c | 163 | ||||
-rw-r--r-- | shed_edit.c | 97 | ||||
-rw-r--r-- | shed_insert.c | 62 | ||||
-rw-r--r-- | shed_io.c | 91 | ||||
-rw-r--r-- | shed_move.c | 45 | ||||
-rw-r--r-- | shed_non_vi.c | 99 | ||||
-rw-r--r-- | umask.c | 27 |
14 files changed, 758 insertions, 51 deletions
@@ -4,7 +4,7 @@ default: all -CFLAGS=-g -Wall -Wextra -Wpedantic +CFLAGS=-g -Wall -Wextra -Wpedantic -D_XOPEN_SOURCE=700 UTILITY=sh SOURCES=alias.c bg.c builtins.c cd.c command.c fc.c fg.c getopts.c \ @@ -12,14 +12,16 @@ SOURCES=alias.c bg.c builtins.c cd.c command.c fc.c fg.c getopts.c \ ulimit.c umask.c wait.c \ dot.c eval.c exec.c exit.c export.c readonly.c shift.c times.c trap.c \ unset.c \ - interactive.c parse.c init.c getopt.c -HEADERS=sh.h + interactive.c parse.c init.c getopt.c \ + shed.c shed_commands.c shed_edit.c shed_insert.c shed_io.c shed_move.c shed_non_vi.c +HEADERS=sh.h shed.h OBJECTS=alias.o bg.o builtins.o cd.o command.o fc.o fg.o getopts.o \ hash.o jobs.o kill.o newgrp.o pwd.o read.o set.o main.o type.o \ ulimit.o umask.o wait.o \ dot.o eval.o exec.o exit.o export.o readonly.o shift.o times.o trap.o \ unset.o \ interactive.o parse.o init.o getopt.o \ + shed.o shed_commands.o shed_edit.o shed_insert.o shed_io.o shed_move.o shed_non_vi.o \ sh.tab.o sh.yy.o BUILTINS=alias bg cd command fc fg getopts jobs newgrp read \ ulimit umask unalias wait @@ -27,7 +29,7 @@ BUILTINS=alias bg cd command fc fg getopts jobs newgrp read \ all: $(UTILITY) $(L10N) $(BUILTINS) sh: $(OBJECTS) $(HEADERS) - $(CC) -o $@ $(CFLAGS) $(OBJECTS) -ly -ll + $(CC) -o $@ $(CFLAGS) $(OBJECTS) $(BUILTINS): $(UTILITY) #ln -s $(UTILITY) $@ diff --git a/interactive.c b/interactive.c index 6db84b0..3f753a3 100644 --- a/interactive.c +++ b/interactive.c @@ -19,46 +19,26 @@ #define _XOPEN_SOURCE 700 -#include "sh.h" #include <stdio.h> #include <stdlib.h> #include <string.h> -static char *sh_getline(char **cmdline) -{ - char *prompt = getenv(*cmdline ? "PS2" : "PS1"); - /* wordexpand prompt */ - fprintf(stderr, "%s", prompt); - - size_t n = 0; - getline(cmdline, &n, stdin); - return *cmdline; -} +#include "sh.h" +#include "shed.h" int sh_interactive(void) { - while (!feof(stdin)) { - char *cmdline = NULL; - sh_getline(&cmdline); - - if (cmdline == NULL) { - break; - } - - struct command *command = sh_parse(cmdline); - #if 0 - while (command == NULL) { - /* append more text */ - /* attempt parsing again */ - } - #endif + struct shed ed = { + .prompt = getenv("PS1"), + .handle = shed_handle_non_vi, + }; + while (shed(&ed) != NULL) { + struct command *command = sh_parse(ed.cur->buf); if (command) { sh_execute(command); sh_freecmd(command); } - - free(cmdline); } return 0; @@ -110,11 +110,10 @@ int set_main(int argc, char *argv[]) break; case ':': - if (optopt == 'o') { - show = 1; - } else { + if (optopt != 'o') { return 1; } + show = 1; break; default: @@ -1,9 +1,7 @@ %{ +#include <stdio.h> #include "sh.h" #include "sh.tab.h" -/* thanks, flex, for implicitly declaring identifiers */ -/* bonus points for something that isn't part of ISO C */ -extern int fileno(FILE *); %} NAME [_a-zA-Z0-9] @@ -65,6 +63,11 @@ OPERATOR [()|;&<>] {WHITESPACE} ; %% +int yywrap(void) +{ + return 1; +} + void sh_silence_warning(void) { input(); @@ -310,3 +310,11 @@ sequential_sep : ';' linebreak { puts("sequential_sep:newline_list"); } ; + +%% + +int yyerror(const char *s) +{ + return fprintf(stderr, "%s\n", s); +} + @@ -0,0 +1,65 @@ +#define _POSIX_C_SOURCE 200809L +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> +#include "shed.h" + +/* + * BIG FAT TODO: DEAL WITH MULTIBYTE CHARACTERS CORRECTLY + */ + +struct shed *shed(struct shed *e) +{ + if (e == NULL) { + e = calloc(1, sizeof(*e)); + if (e == NULL) { + return NULL; + } + } + + /* TODO: head/tail/history */ + if (e->cur == NULL) { + e->cur = calloc(1, sizeof(*e->cur)); + if (e->cur == NULL) { + return e; + } + e->handle = shed_handle_non_vi; + } + + memset(e->cur, '\0', sizeof(*e->cur)); + + struct termios original_tio; + if (tcgetattr(STDIN_FILENO, &original_tio) != 0) { + e->cur = NULL; + return e; + } + + struct termios tio = original_tio; + tio.c_iflag &= ~BRKINT; + tio.c_lflag &= ~ECHO; + tio.c_lflag &= ~IEXTEN; + tio.c_lflag &= ~ICANON; + tio.c_lflag &= ~ISIG; + tcsetattr(STDIN_FILENO, TCSADRAIN, &tio); + + if (e->prompt == NULL) { + e->prompt = getenv("PS1"); + if (e->prompt == NULL) { + e->prompt = "$ "; + } + } + write(STDOUT_FILENO, e->prompt, strlen(e->prompt)); + + char c; + do { + if (read(STDIN_FILENO, &c, 1) == -1) { + //b.nread = -1; + /* TODO: signal failure */ + break; + } + } while (e->handle(e, &tio, c)); + + tcsetattr(STDIN_FILENO, TCSADRAIN, &original_tio); + return e; +} @@ -0,0 +1,92 @@ +#ifndef SHED_H +#define SHED_H + +#include <sys/types.h> + +enum { + CTRL_A = 0x01, + CTRL_B = 0x02, + CTRL_C = 0x03, + CTRL_D = 0x04, + CTRL_E = 0x05, + CTRL_F = 0x06, + CTRL_G = 0x07, + CTRL_H = 0x08, + CTRL_I = 0x09, + CTRL_J = 0x0a, + CTRL_K = 0x0b, + CTRL_L = 0x0c, + CTRL_M = 0x0d, + CTRL_N = 0x0e, + CTRL_O = 0x0f, + CTRL_P = 0x10, + CTRL_Q = 0x11, + CTRL_R = 0x12, + CTRL_S = 0x13, + CTRL_T = 0x14, + CTRL_U = 0x15, + CTRL_V = 0x16, + CTRL_W = 0x17, + CTRL_X = 0x18, + CTRL_Y = 0x19, + CTRL_Z = 0x1a, + ESCAPE = 0x1b, +}; + +struct termios; + +struct buffer { + size_t len; + size_t pos; + ssize_t nread; + char buf[512]; /* TODO: dynamic */ +}; + +struct shed { + char *prompt; + int (*handle)(struct shed *e, struct termios *, char); + struct buffer *head; + struct buffer *tail; + struct buffer *cur; + int count; +}; + +void shed_replace_char(struct buffer *b, char c); +void shed_insert_char(struct buffer *b, char c); +void shed_remove_char(struct buffer *b, int forward); +void shed_move_cursor(struct buffer *b, int move); + +int shed_append(struct shed *e); +int shed_append_end(struct shed *e); +int shed_backspace(struct shed *e); +int shed_cancel(struct shed *e); +int shed_change(struct shed *e); +int shed_comment(struct shed *e); +int shed_convert_case(struct shed *e); +int shed_eof(struct shed *e); +int shed_delete(struct shed *e); +int shed_delete_toend(struct shed *e); +int shed_erase(struct shed *e); +int shed_execute(struct shed *e); +int shed_insert(struct shed *e); +int shed_insert_beginning(struct shed *e); +int shed_redraw(struct shed *e); +int shed_replace(struct shed *e); +int shed_start_over(struct shed *e); +int shed_worderase(struct shed *e); + +int shed_move_forward(struct shed *e); +int shed_move_backward(struct shed *e); +int shed_move_beginning(struct shed *e); +int shed_move_end(struct shed *e); +int shed_move_0(struct shed *e); +int shed_move_column(struct shed *e); + +int shed_handle_insert(struct shed *e, struct termios *t, char c); +int shed_handle_replace(struct shed *e, struct termios *t, char c); +int shed_handle_edit(struct shed *e, struct termios *t, char c); +int shed_handle_non_vi(struct shed *e, struct termios *t, char c); + +struct shed *shed(struct shed *e); + +#endif diff --git a/shed_commands.c b/shed_commands.c new file mode 100644 index 0000000..0c2bdfc --- /dev/null +++ b/shed_commands.c @@ -0,0 +1,163 @@ +#define _POSIX_C_SOURCE 200809L +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "shed.h" + +int shed_append(struct shed *e) +{ + e->count = 1; + shed_move_forward(e); + return shed_insert(e); +} + +int shed_append_end(struct shed *e) +{ + shed_move_end(e); + return shed_insert(e); +} + +int shed_backspace(struct shed *e) +{ + int count = e->count ? e->count : 1; + for (int i = 0; i < count; i++) { + shed_remove_char(e->cur, 0); + } + return 1; +} + +int shed_cancel(struct shed *e) +{ + struct buffer *b = e->cur; + write(STDOUT_FILENO, "^C", 2); + b->nread = 0; + return 0; +} + +int shed_change(struct shed *e) +{ + shed_delete_toend(e); + return shed_append_end(e); +} + +int shed_comment(struct shed *e) +{ + shed_move_cursor(e->cur, -e->cur->pos); + shed_insert_char(e->cur, '#'); + return 0; +} + +int shed_convert_case(struct shed *e) +{ + char *b = e->cur->buf; + size_t pos = e->cur->pos; + int count = e->count ? e->count : 1; + + /* TODO: don't overflow */ + for (int i = 0; i < count && pos + i < (size_t)e->cur->nread; i++) { + if (isupper(b[pos + i])) { + shed_replace_char(e->cur, tolower(b[pos + i])); + } else if (islower(b[pos + i])) { + shed_replace_char(e->cur, toupper(b[pos + i])); + } else { + shed_replace_char(e->cur, b[pos + i]); + } + } + return 1; +} + +int shed_delete(struct shed *e) +{ + int count = e->count ? e->count : 1; + for (int i = 0; i < count; i++) { + shed_remove_char(e->cur, 1); + } + return 1; +} + +int shed_delete_toend(struct shed *e) +{ + while (e->cur->pos < (size_t)e->cur->nread) { + shed_remove_char(e->cur, 1); + } + return 1; +} + +int shed_eof(struct shed *e) +{ + struct buffer *b = e->cur; + if (b->nread == 0) { + b->buf[0] = CTRL_D; + b->nread = 1; + return 0; + } + return 1; +} + +int shed_erase(struct shed *e) +{ + struct buffer *b = e->cur; + while (b->pos != 0) { + shed_remove_char(b, 0); + } + return 1; +} + +int shed_execute(struct shed *e) +{ + struct buffer *b = e->cur; + b->pos = b->nread; + shed_insert_char(b, '\n'); + if (e->handle == shed_handle_edit) { + e->handle = shed_handle_insert; + } + return 0; +} + +int shed_insert(struct shed *e) +{ + e->handle = shed_handle_insert; + return 1; +} + +int shed_insert_beginning(struct shed *e) +{ + shed_move_0(e); + return shed_insert(e); +} + +int shed_redraw(struct shed *e) +{ + system("tput clear"); // TODO: pfork() and cache the output + write(STDOUT_FILENO, e->prompt, strlen(e->prompt)); + write(STDOUT_FILENO, e->cur->buf, e->cur->nread); + int move = e->cur->nread - e->cur->pos; + e->cur->pos = e->cur->nread; + shed_move_cursor(e->cur, -move); + return 1; +} + +int shed_replace(struct shed *e) +{ + e->handle = shed_handle_replace; + return 1; +} + +int shed_start_over(struct shed *e) +{ + shed_move_0(e); + return shed_change(e); +} + +int shed_worderase(struct shed *e) +{ + struct buffer *b = e->cur; + while (b->pos != 0 && (isblank(b->buf[b->pos - 1]) || ispunct(b->buf[b->pos - 1]))) { + shed_remove_char(b, 0); + } + while (b->pos != 0 && !(isspace(b->buf[b->pos - 1]) || ispunct(b->buf[b->pos - 1]))) { + shed_remove_char(b, 0); + } + return 1; +} diff --git a/shed_edit.c b/shed_edit.c new file mode 100644 index 0000000..44af225 --- /dev/null +++ b/shed_edit.c @@ -0,0 +1,97 @@ +#define _POSIX_C_SOURCE 200809L +#include <ctype.h> +#include <stdlib.h> +#include "shed.h" + +int shed_handle_edit(struct shed *e, struct termios *t, char c) +{ + (void)t; + int r = -1; + + switch (c) { + case '\n': r = shed_execute(e); break; + case CTRL_L: r = shed_redraw(e); break; + case '#': r = shed_comment(e); break; + + case '=': // r = shed_complete_wordexp(e); break; + case '\\': // r = shed_complete_pathname(e); break; + case '*': // r = shed_complete_wildcard(e); break; + + case '@': // r = shed_insert_alias(e); // read one letter + + case '~': r = shed_convert_case(e); break; + case '.': // r = shed_repeat(e); break; + case 'v': // r = shed_invoke_vi(e); break; + + case 'l': /* FALLTHROUGH */ + case ' ': r = shed_move_forward(e); break; + case 'h': r = shed_move_backward(e); break; + case 'w': // r = shed_move_forword(e); break; + case 'W': // r = shed_move_forbigword(e); break; + case 'e': // r = shed_move_endword(e); break; + case 'E': // r = shed_move_endbigword(e); break; + case 'b': // r = shed_move_backword(e); break; + case 'B': // r = shed_move_backbigword(e); break; + case '^': r = shed_move_beginning(e); break; + case '$': r = shed_move_end(e); break; + case '0': r = shed_move_0(e); break; + case '|': r = shed_move_column(e); break; + + case 'f': // r = shed_move_first(e); break; + case 'F': // r = shed_move_prev(e); break; + case 't': // r = shed_move_before(e); break; + case 'T': // r = shed_move_after(e); break; + case ';': // r = shed_repeat_fftt(e); break; + case ',': // r = shed_reverse_fftt(e); break; + + case 'a': r = shed_append(e); break; + case 'A': r = shed_append_end(e); break; + case 'i': r = shed_insert(e); break; + case 'I': r = shed_insert_beginning(e); break; + case 'R': r = shed_replace(e); break; + + case 'c': // r = shed_delete_to(e); break; + case 'C': r = shed_change(e); break; + case 'S': r = shed_start_over(e); break; + + case 'r': // r = shed_replace(e); break; + case '_': // ??? + case 'x': r = shed_delete(e); break; + case 'X': r = shed_backspace(e); break; + case 'd': // r = shed_delete_motion(e); break; + case 'D': r = shed_delete_toend(e); break; + case 'y': // r = shed_yank(e); break; + case 'Y': // r = shed_bigyank(e); break; + case 'p': // r = shed_paste(e); break; + case 'P': // r = shed_bigpaste(e); break; + + case 'u': // r = shed_undo(e); break; + case 'U': // r = shed_bigundo(e); break; + + case 'k': /* FALLTHROUGH */ + case '-': // r = shed_history_back(e); break; + + case 'j': /* FALLTHOUGH */ + case '+': // r = shed_history_forward(e); break; + + case 'G': // r = shed_history_first(e); break; + case '/': // r = shed_history_backsearch(e); break; + case '?': // r = shed_history_forwardsearch(e); break; + case 'n': // r = shed_history_repeat(e); break; + case 'N': // r = shed_history_reverse(e); break; + + default: + break; + } + + if (isdigit(c)) { + char digit[2] = { c, '\0' }; + e->count = (e->count * 10) + atoi(digit); + } + + if (r != -1) { + e->count = 0; + } + + return r; +} diff --git a/shed_insert.c b/shed_insert.c new file mode 100644 index 0000000..384cbea --- /dev/null +++ b/shed_insert.c @@ -0,0 +1,62 @@ +#define _POSIX_C_SOURCE 200809L +#include <termios.h> +#include "shed.h" + +static int shed_insert_special(struct shed *e, struct termios *t, char c) +{ + if (c == '\n') { + return shed_execute(e); + } + + if (c == t->c_cc[VERASE]) { + return shed_backspace(e); + } + + if (c == t->c_cc[VINTR]) { + return shed_cancel(e); + } + + if (c == t->c_cc[VKILL]) { + return shed_erase(e); + } + + if (c == CTRL_V) { + /* quote the next character */ + return 1; + } + + if (c == CTRL_W) { + return shed_worderase(e); + } + + if (c == t->c_cc[VEOF]) { + return shed_eof(e); + } + + if (c == ESCAPE) { + e->handle = shed_handle_edit; + return 1; + } + + return -1; +} + +int shed_handle_insert(struct shed *e, struct termios *t, char c) +{ + int r = shed_insert_special(e, t, c); + if (r == -1) { + shed_insert_char(e->cur, c); + r = 1; + } + return r; +} + +int shed_handle_replace(struct shed *e, struct termios *t, char c) +{ + int r = shed_insert_special(e, t, c); + if (r == -1) { + shed_replace_char(e->cur, c); + r = 1; + } + return r; +} diff --git a/shed_io.c b/shed_io.c new file mode 100644 index 0000000..17aee8e --- /dev/null +++ b/shed_io.c @@ -0,0 +1,91 @@ +#define _POSIX_C_SOURCE 200809L +#include <string.h> +#include <unistd.h> +#include "shed.h" + +void shed_replace_char(struct buffer *b, char c) +{ + if (b->pos >= (size_t)b->nread) { + shed_insert_char(b, c); + return; + } + b->buf[b->pos] = c; + write(STDOUT_FILENO, &c, 1); + b->pos++; +} + +void shed_insert_char(struct buffer *b, char c) +{ + if (b->pos >= (size_t)b->nread) { + b->buf[b->pos] = c; + write(STDOUT_FILENO, &c, 1); + } else { + size_t shift = b->nread - b->pos; + char *start = b->buf + b->pos; + memmove(start + 1, start, shift); + *start = c; + + char back[shift]; + memset(back, '\b', shift); + write(STDOUT_FILENO, start, shift + 1); + write(STDOUT_FILENO, back, shift); + } + + b->pos++; + b->nread++; +} + +void shed_remove_char(struct buffer *b, int forward) +{ + /* TODO: don't forward delete past the end */ + if (forward) { + write(STDOUT_FILENO, b->buf + b->pos, 1); + b->pos++; + } + + if (b->pos == 0) { + return; + } + + b->pos--; + + char *start = b->buf + b->pos; + size_t shift = (size_t)b->nread - b->pos; + + b->nread--; + + for (size_t i = 0; i < shift; i++) { + start[i] = start[i + 1]; + } + start[shift] = '\0'; + + char back[shift]; + memset(back, '\b', shift); + write(STDOUT_FILENO, back, 1); + write(STDOUT_FILENO, start, shift); + write(STDOUT_FILENO, " \b", 2); + write(STDOUT_FILENO, back, shift - 1); +} + +void shed_move_cursor(struct buffer *b, int move) +{ + if (move > 0) { + int maxfwd = (int)(b->nread - b->pos); + if (move > maxfwd) { + move = maxfwd; + } + write(STDOUT_FILENO, b->buf + b->pos, move); + b->pos += move; + } + + if (move < 0) { + size_t len = (size_t)(-move); + if (len > b->pos) { + len = b->pos; + } + char back[len]; + memset(back, '\b', len); + write(STDOUT_FILENO, back, len); + b->pos -= len; + } +} diff --git a/shed_move.c b/shed_move.c new file mode 100644 index 0000000..0970085 --- /dev/null +++ b/shed_move.c @@ -0,0 +1,45 @@ +#define _POSIX_C_SOURCE 200809L +#include <ctype.h> +#include "shed.h" + +int shed_move_forward(struct shed *e) +{ + shed_move_cursor(e->cur, e->count ? e->count : 1); + return 1; +} + +int shed_move_backward(struct shed *e) +{ + shed_move_cursor(e->cur, e->count ? -e->count : -1); + return 1; +} + +int shed_move_beginning(struct shed *e) +{ + shed_move_cursor(e->cur, -e->cur->pos); + while (isspace(e->cur->buf[e->cur->pos])) { + shed_move_cursor(e->cur, 1); + } + return 1; +} + +int shed_move_end(struct shed *e) +{ + shed_move_cursor(e->cur, e->cur->nread); + return 1; +} + +int shed_move_0(struct shed *e) +{ + shed_move_cursor(e->cur, -e->cur->pos); + return 1; +} + +int shed_move_column(struct shed *e) +{ + if (e->count == 0) { + e->count = 1; + } + shed_move_cursor(e->cur, e->count - e->cur->pos - 1); + return 1; +} diff --git a/shed_non_vi.c b/shed_non_vi.c new file mode 100644 index 0000000..eda2cc6 --- /dev/null +++ b/shed_non_vi.c @@ -0,0 +1,99 @@ +#define _POSIX_C_SOURCE 200809L +#include <ctype.h> +#include <termios.h> +#include <unistd.h> +#include "shed.h" + +int shed_handle_non_vi(struct shed *e, struct termios *t, char c) +{ + if (c == t->c_cc[VEOF]) { + if (shed_eof(e) == 0) { + return 0; + } + } + + if (c == t->c_cc[VINTR]) { + return shed_cancel(e); + } + + if (c == t->c_cc[VEOL]) { + return shed_move_end(e); + } + + if (c == t->c_cc[VERASE]) { + return shed_backspace(e); + } + + if (c == t->c_cc[VQUIT]) { + /* ^\ */ + return 1; + } + + if (c == t->c_cc[VSTART]) { + /* ^Q */ + return 1; + } + + if (c == t->c_cc[VSTOP]) { + /* ^S */ + return 1; + } + + if (c == t->c_cc[VSUSP]) { + /* ^Z */ + return 1; + } + + if (c == '\033') { + char esc[2]; + read(STDIN_FILENO, esc, 2); + if (esc[1] == 'C') { + return shed_move_forward(e); + } + if (esc[1] == 'D' && e->cur->pos > 0) { + return shed_move_backward(e); + } + return 1; + } + + if (c == '\n') { + return shed_execute(e); + } + + if (CTRL_A <= c && c <= CTRL_Z) { + switch (c) { + case CTRL_A: return shed_move_0(e); + case CTRL_B: return shed_move_backward(e); + case CTRL_C: return shed_cancel(e); + case CTRL_D: return shed_delete(e); + case CTRL_E: return shed_move_end(e); + case CTRL_F: return shed_move_forward(e); + case CTRL_G: break; // return abort(e)? + case CTRL_H: return shed_backspace(e); + case CTRL_I: break; // return complete_wordexp(e); + case CTRL_J: return shed_execute(e); + case CTRL_K: return shed_delete_toend(e); + case CTRL_L: return shed_redraw(e); + case CTRL_M: return shed_execute(e); + case CTRL_N: // return history_forward(e); + case CTRL_O: // return ??? /* ??? */ + case CTRL_P: // return history_backward(e); + case CTRL_Q: // /* TTY START */ + case CTRL_R: // return history_backsearch(e); + case CTRL_S: // /* TTY STOP */ + case CTRL_T: break; // /* ??? */ + case CTRL_U: return shed_erase(e); + case CTRL_V: break; // return quote(e); + case CTRL_W: return shed_worderase(e); + case CTRL_X: // /* ??? */ + case CTRL_Y: // /* ??? */ + case CTRL_Z: // /* ??? */ + break; + } + return 1; + } + + /* regular character */ + shed_insert_char(e->cur, c); + return 1; +} @@ -47,19 +47,20 @@ int umask_main(int argc, char *argv[]) umask(mask); if (symbolic) { - fputs("u=", stdout); - fputs(mask & S_IRUSR ? "" : "r", stdout); - fputs(mask & S_IWUSR ? "" : "w", stdout); - fputs(mask & S_IXUSR ? "" : "x", stdout); - fputs(",g=", stdout); - fputs(mask & S_IRGRP ? "" : "r", stdout); - fputs(mask & S_IWGRP ? "" : "w", stdout); - fputs(mask & S_IXGRP ? "" : "x", stdout); - fputs(",o=", stdout); - fputs(mask & S_IRGRP ? "" : "r", stdout); - fputs(mask & S_IWGRP ? "" : "w", stdout); - fputs(mask & S_IXGRP ? "" : "x", stdout); - putchar('\n'); + printf("u="); + printf("%s", mask & S_IRUSR ? "" : "r"); + printf("%s", mask & S_IWUSR ? "" : "w"); + printf("%s", mask & S_IXUSR ? "" : "x"); + + printf(",g="); + printf("%s", mask & S_IRGRP ? "" : "r"); + printf("%s", mask & S_IWGRP ? "" : "w"); + printf("%s", mask & S_IXGRP ? "" : "x"); + + printf("%s", ",o="); + printf("%s", mask & S_IRGRP ? "" : "r"); + printf("%s", mask & S_IWGRP ? "" : "w"); + printf("%s\n", mask & S_IXGRP ? "" : "x"); } else { printf("%04o\n", mask); } |