#define _POSIX_C_SOURCE 200809l #include #include #include #include #include #include #include "config.h" #include "fstr.h" #include "err.h" #define S_ESCAPE 0x1 fstr_t PS1 = {0}; unsigned int histsize = DEF_HISTSIZE; // Buffered user data struct { char *name; char *home; uid_t uid; gid_t gid; } uinfo = {0}; struct { // $ or # char tag; } shinfo; struct { struct termios tio; struct termios tio_before; } terminfo; void sh_setup_terminal(void); void sh_update_uinfo(void); void sh_update_shinfo(void); void sh_update_ps1(void); inline void sh_print_ps1(void); int main(int argc, char **argv) { (void)argc; (void)argv; char i; unsigned int state = 0; sh_setup_terminal(); for (;;) { sh_update_uinfo(); sh_update_shinfo(); sh_update_ps1(); sh_print_ps1(); fflush(stdout); for (int line = 1; line;) { read(STDIN_FILENO, &i, 1); // This blocks fputc(i, stdout); fflush(stdout); switch (i) { case '\n': line = state & S_ESCAPE; break; case '\\': state ^= ~S_ESCAPE; break; default: state &= ~S_ESCAPE; break; } } } return EXIT_SUCCESS; } void sh_setup_terminal(void) { if (tcgetattr(STDOUT_FILENO, &terminfo.tio_before) == -1) die("tcgetattr:"); terminfo.tio = terminfo.tio_before; /* Do not echo and input is available immediately */ terminfo.tio.c_lflag &= ~ECHO; terminfo.tio.c_lflag &= ~ICANON; if (tcsetattr(STDOUT_FILENO, TCSANOW, &terminfo.tio) == -1) die("tcsetattr:"); } void sh_update_uinfo(void) { struct passwd *pw; uid_t nuid; nuid = getuid(); if (nuid == uinfo.uid && uinfo.name) return; uinfo.uid = nuid; pw = getpwuid(uinfo.uid); // User not found if (!pw) die("getpwuid:"); uinfo.gid = pw->pw_gid; uinfo.name = estrdup(pw->pw_name); uinfo.home = estrdup(pw->pw_dir); } void sh_update_shinfo(void) { shinfo.tag = uinfo.uid ? '$' : '#'; } void sh_print_ps1(void) { fputs(PS1.s, stdout); } void sh_update_ps1(void) { fstr_clear(&PS1); fstr_append_char(&PS1, shinfo.tag); fstr_append_char(&PS1, ' '); }