#define MALDEBUG #include #include #include #include #include #include #include #include #include #include #include #include /* Until we get a proper O_NOACCESS... */ #ifndef O_NOACCESS #define O_NOACCESS O_RDONLY #endif extern const char *__progname; typedef unsigned short int CHF; #define CHF_CHAR 0x00ff #define CHF_TERM 0x0100 #define CHF_BQUOTE 0x0200 #define CHF_SQUOTE 0x0400 #define CHF_DQUOTE 0x0800 #define CHF_QUOTE (CHF_BQUOTE|CHF_SQUOTE|CHF_DQUOTE) typedef struct priminit PRIMINIT; typedef struct defbind DEFBIND; typedef struct keyfunc KEYFUNC; typedef struct key KEY; typedef struct keymap KEYMAP; typedef struct histlist HISTLIST; typedef struct histent HISTENT; typedef struct cwd CWD; typedef struct environment ENVIRONMENT; typedef struct path PATH; typedef struct shellstate SHELLSTATE; typedef struct charset CHARSET; typedef struct job JOB; typedef struct jobkid JOBKID; typedef struct token TOKEN; typedef struct pt PT; typedef struct wordpart WORDPART; typedef struct ptrlist PTRLIST; typedef struct ppipe PPIPE; typedef struct exitstat EXITSTAT; typedef struct builtin BUILTIN; typedef struct fdenv FDENV; typedef struct fdfd FDFD; typedef enum fdfd_t FDFD_T; enum fdfd_t { FDFD_NONE = 1, FDFD_CLOSE, FDFD_SHELL, FDFD_FILE, FDFD_PIPE, FDFD_BQOUT, } ; struct fdfd { int fd; FDFD_T type; union { int shfd; /* SHELL */ PT *redir; /* FILE, PIPE */ PT *bq; /* BQOUT */ } u; } ; struct fdenv { int n; int a; FDFD *list; int refs; unsigned int flags; #define FDEF_PROCESSED 0x00000001 } ; struct builtin { const char *name; void (*fn)(PT *); int namelen; } ; #define BIFN(fn) static void fn(PT *wl) typedef enum exitstat_t EXITSTAT_T; enum exitstat_t { ET_EXIT = 1, ET_SIG, } ; struct exitstat { EXITSTAT_T type; union { int exit; struct { int sig; int core; } sig; } u; } ; struct ppipe { PT *name; int fds[2]; int refs[2]; } ; struct ptrlist { PTRLIST *link; void *p; } ; typedef enum wordpart_t WORDPART_T; enum wordpart_t { WPT_TEXT = 1, WPT_BQ, } ; struct wordpart { WORDPART_T type; union { TOKEN *text; PT *bq; } u; } ; typedef enum pt_t PT_T; enum pt_t { PT_NONE = 1, PT_CMDLINE, PT_COMMAND, PT_WORDLIST, PT_SEQUENCE, PT_OROR, PT_ANDAND, PT_PIPELINE, PT_REDIR, PT_WORD, PT_BQ, } ; typedef enum pt_command_t PT_COMMAND_T; enum pt_command_t { PT_CMD_NONE = 1, PT_CMD_SIMPLE, PT_CMD_PAREN, PT_CMD_SUBSHELL, PT_CMD_PARALLEL, PT_CMD_NOWAIT, PT_CMD_BG, PT_CMD_COND, } ; typedef enum pt_redir_t PT_REDIR_T; enum pt_redir_t { PT_REDIR_NONE = 1, PT_REDIR_FILE, PT_REDIR_FILEAND, PT_REDIR_DUP, PT_REDIR_PIPE, } ; struct pt { PT_T type; unsigned char flags; #define PTF_STARTED 0x01 #define PTF_DONE 0x02 #define PTF_PRUNE 0x04 #define PTF_SUCCESS 0x08 union { struct { PT *seq; unsigned int bg : 1; int ntok; TOKEN **toks; } cmdline; struct { PT_COMMAND_T type; int n_redir; PT **redirs; FDENV *env; union { struct { PT *wordlist; JOBKID *p; } simple; PT *seq; /* PAREN, NOWAIT, BG */ struct { PT *seq; SHELLSTATE *state; } subshell; struct { PT *ll_thr; PT *name; int n_pipes; PPIPE *pipes; int n_seq; PT **seqs; } parallel; struct { PT *test; PT *ift; PT *iff; } cond; } u; } command; struct { unsigned int flags; #define PT_WLF_HASBQ 0x00000001 #define PT_WLF__ALL 0x00000001 int n; PT **list; } wl; /* WORDLIST */ struct { int n; PT **list; } pts; /* SEQUENCE, OROR, ANDAND, PIPELINE */ struct { PT_REDIR_T type; PT *r_thr; int refcnt; int usecnt; int n; union { struct { int oflags; PT *word; int shfd; int refs; } file; struct { int oflags; PT *word; } fileand; struct { int m; #define DUP_SPECIAL(x) ((x)<0) #define DUP_CLOSE (-1) PT *dupthr; } dup; struct { PT *word; PT *parallel; int pipeno; #define PIPENO_SPECIAL(x) ((x)<0) #define PIPENO_COPY (-1) int which; } pipe; } u; } redir; struct { unsigned int flags; #define PT_WF_GLOBONE 0x00000001 #define PT_WF_GLOBMANY 0x00000002 #define PT_WF_ANYBQ 0x00000004 #define PT_WF_QUOTE 0x00000008 #define PT_WF_TEXT 0x00000010 #define PT_WF__ALL 0x0000001f int len; union { WORDPART *parts; char *text; } u; } word; struct { unsigned int bqflags; #define PT_BQF_Q_ALL 0x00000001 #define PT_BQF_NEWLINE 0x00000002 #define PT_BQF_Z_LEAD 0x00000004 #define PT_BQF_Z_TRAIL 0x00000008 #define PT_BQF_Z_OTHER 0x00000010 #define PT_BQF_ERROR 0x00000020 #define PT_BQF__BQALL 0x0000003f unsigned int rtflags; #define PT_BQF_HAVE_R 0x00000001 #define PT_BQF_HAVE_W 0x00000002 #define PT_BQF_GOTEOF 0x00000004 #define PT_BQF_LISTED 0x00000008 #define PT_BQF__RTALL 0x0000000f PT *seq; PT *flink; PT *blink; int pipe[2]; int px; char *buf; int len; } bq; } u; } ; typedef enum token_t TOKEN_T; enum token_t { TOK_EOL = 1, TOK_WHITESPACE, TOK_WORDTEXT, TOK_AND, /* & */ TOK_LPAR, /* ( */ TOK_RPAR, /* ) */ TOK_LBANG, /* (! */ TOK_PLEFT, /* (| */ TOK_PCOLON, /* |: */ TOK_PMINUS, /* |- */ TOK_PSEMI, /* |; */ TOK_PRIGHT, /* |) */ TOK_ANDL, /* (& */ TOK_ANDANDL, /* (&& */ TOK_ANDR, /* &) */ TOK_SEMI, /* ; */ TOK_OROR, /* || */ TOK_ANDAND, /* && */ TOK_PIPE, /* | */ TOK_LT, /* < */ TOK_GT, /* > */ TOK_GGT, /* >> */ TOK_GTAND, /* >& */ TOK_GGTAND, /* >>& */ TOK_N_LT, /* N< */ TOK_N_GT, /* N> */ TOK_N_GGT, /* N>> */ TOK_N_LTGT, /* N<> */ TOK_N_LTGGT, /* N<>> */ TOK_N_AND_M, /* N&M */ TOK_N_AND_NIL, /* N&- */ TOK_LTPIPE, /* <| */ TOK_GTPIPE, /* >| */ TOK_N_LTPIPE, /* N<| */ TOK_N_GTPIPE, /* N>| */ TOK_LTPPIPE, /* <|| */ TOK_GTPPIPE, /* >|| */ TOK_N_LTPPIPE, /* N<|| */ TOK_N_GTPPIPE, /* N>|| */ TOK_LBQQ, /* (`` */ TOK_LBQ, /* (` */ TOK_RBQ, /* `) */ TOK_CIF, /* (? */ TOK_CTHEN, /* ?- */ TOK_CELSE, /* ?; */ } ; struct token { TOKEN_T type; union { struct { int n; int m; } n; struct { unsigned int flags; #define TOK_WF_QUOTE 0x00000001 int l; unsigned char *s; CHF *sf; } w; } u; } ; typedef enum jobkid_s JOBKID_S; enum jobkid_s { JKS_RUN = 1, JKS_STOP, JKS_DEAD, } ; struct jobkid { JOBKID *link; JOB *j; pid_t pid; PT *pt; JOBKID_S state; union { EXITSTAT x; int s; } u; } ; struct job { JOB *flink; JOB *blink; unsigned int flags; #define JF_HAVEPIPE 0x00000001 #define JF_HAVEPG 0x00000002 #define JF_HAVEPROC 0x00000004 int gopipe[2]; pid_t pg; int procpipe; pid_t proc; JOBKID *livekids; } ; struct charset { const char **names; unsigned char *flags; #define CTF_PRINTABLE 0x01 #define CTF_WORDCHAR 0x02 #define CTF_LETTER 0x0c #define CTF_LETTER_NOT 0x00 #define CTF_LETTER_LC 0x04 #define CTF_LETTER_UC 0x08 #define CTF_LETTER_OTH 0x0c #define CTF_CTLCHAR 0x10 #define CTF_WHITESPACE 0x20 unsigned char *disp; unsigned char *upcase; unsigned char *dncase; } ; struct shellstate { int debug; CWD *cwd; ENVIRONMENT *env; PATH *path; CHARSET *charset; } ; struct path { int refcnt; int nstrs; char **strs; char *strbuf; int maxlen; } ; struct environment { int refcnt; char **vars; int nvars; int nvalloc; } ; struct cwd { int refcnt; int fd; } ; struct histent { HISTENT *older; HISTENT *newer; unsigned char *txt; int len; } ; struct histlist { HISTENT *root; int *sizep; } ; typedef enum key_t KEY_T; enum key_t { KT_UNBOUND = 1, KT_LEAF, KT_MAP, } ; struct key { KEY_T type; union { KEYFUNC *leaf; KEYMAP *map; } u; } ; struct keymap { KEY key[256]; } ; struct keyfunc { KEYFUNC *link; const char *name; unsigned char type; #define KFT_BUILTIN 1 union { void (*builtin)(void); } u; } ; struct defbind { const char *key; const char *binding; } ; struct priminit { const char *name; void (*fxn)(void); } ; static const char *charset_iso_8859_1_names[] = { "iso-8859-1", "iso-latin1", "8859-1", "latin-1", 0 }; static unsigned char charset_iso_8859_1_flags[256] #define C (CTF_CTLCHAR) #define P (CTF_PRINTABLE) #define W (CTF_PRINTABLE|CTF_WORDCHAR) #define U (CTF_PRINTABLE|CTF_WORDCHAR|CTF_LETTER_UC) #define L (CTF_PRINTABLE|CTF_WORDCHAR|CTF_LETTER_LC) #define O (CTF_PRINTABLE|CTF_WORDCHAR|CTF_LETTER_OTH) #define S (CTF_PRINTABLE|CTF_WHITESPACE) #define s (CTF_CTLCHAR|CTF_WHITESPACE) #define w (CTF_WHITESPACE) = { C,C,C,C,C,C,C,C, C,s,s,C,C,C,C,C, C,C,C,C,C,C,C,C, C,C,C,C,C,C,C,C, S,P,P,P,W,P,P,P, P,P,P,P,P,W,W,P, W,W,W,W,W,W,W,W, W,W,P,P,P,P,P,P, P,U,U,U,U,U,U,U, U,U,U,U,U,U,U,U, U,U,U,U,U,U,U,U, U,U,U,P,P,P,P,W, P,L,L,L,L,L,L,L, L,L,L,L,L,L,L,L, L,L,L,L,L,L,L,L, L,L,L,P,P,P,P,C, C,C,C,C,C,C,C,C, C,C,C,C,C,C,C,C, C,C,C,C,C,C,C,C, C,C,C,C,C,C,C,C, w,P,P,P,P,P,P,P, P,P,P,P,P,P,P,P, P,P,P,P,P,P,P,P, P,P,P,P,P,P,P,P, U,U,U,U,U,U,U,U, U,U,U,U,U,U,U,U, U,U,U,U,U,U,U,P, U,U,U,U,U,U,U,O, L,L,L,L,L,L,L,L, L,L,L,L,L,L,L,L, L,L,L,L,L,L,L,P, L,L,L,L,L,L,L,O }; #undef C #undef P #undef W #undef U #undef L #undef O #undef S #undef s #undef w static unsigned char charset_iso_8859_1_disp[256] = { '@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', 'P','Q','R','S','T','U','V','W','X','Y','Z','[','\\',']','^','_', 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,'?', 'À','Á','Â','Ã','Ä','Å','Æ','Ç','È','É','Ê','Ë','Ì','Í','Î','Ï', 'Ð','Ñ','Ò','Ó','Ô','Õ','Ö','×','Ø','Ù','Ú','Û','Ü','Ý','Þ','ß', 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 }; static unsigned char charset_iso_8859_1_upcase[256] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', 'P','Q','R','S','T','U','V','W','X','Y','Z', 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 'À','Á','Â','Ã','Ä','Å','Æ','Ç','È','É','Ê','Ë','Ì','Í','Î','Ï', 'Ð','Ñ','Ò','Ó','Ô','Õ','Ö', 0,'Ø','Ù','Ú','Û','Ü','Ý','Þ', 0 }; static unsigned char charset_iso_8859_1_dncase[256] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o', 'p','q','r','s','t','u','v','w','x','y','z', 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 'à','á','â','ã','ä','å','æ','ç','è','é','ê','ë','ì','í','î','ï', 'ð','ñ','ò','ó','ô','õ','ö', 0,'ø','ù','ú','û','ü','ý','þ', 0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 }; static CHARSET charset_iso_8859_1 = { &charset_iso_8859_1_names[0], &charset_iso_8859_1_flags[0], &charset_iso_8859_1_disp[0], &charset_iso_8859_1_upcase[0], &charset_iso_8859_1_dncase[0] }; static const char *charnames[] = { "\000^@", "\000NUL", "\000nul", "\001^A", "\001SOH", "\001^a", "\001soh", "\002^B", "\002STX", "\002^b", "\002stx", "\003^C", "\003ETX", "\003^c", "\003etx", "\004^D", "\004EOT", "\004^d", "\004eot", "\005^E", "\005ENQ", "\005^e", "\005enq", "\006^F", "\006ACK", "\006^f", "\006ack", "\007^G", "\007BEL", "\007^g", "\007bel", "\010^H", "\010BS", "\010^h", "\010bs", "\011^I", "\011HT", "\011^i", "\011ht", "\012^J", "\012NL", "\012^j", "\012nl", "\013^K", "\013VT", "\013^k", "\013vt", "\014^L", "\014NP", "\014^l", "\014np", "\015^M", "\015CR", "\015^m", "\015cr", "\016^N", "\016SO", "\016^n", "\016so", "\017^O", "\017SI", "\017^o", "\017si", "\020^P", "\020DLE", "\020^p", "\020dle", "\021^Q", "\021DC1", "\021^q", "\021dc1", "\022^R", "\022DC2", "\022^r", "\022dc2", "\023^S", "\023DC3", "\023^s", "\023dc3", "\024^T", "\024DC4", "\024^t", "\024dc4", "\025^U", "\025NAK", "\025^u", "\025nak", "\026^V", "\026SYN", "\026^v", "\026syn", "\027^W", "\027ETB", "\027^w", "\027etb", "\030^X", "\030CAN", "\030^x", "\030can", "\031^Y", "\031EM", "\031^y", "\031em", "\032^Z", "\032SUB", "\032^z", "\032sub", "\033^[", "\033ESC", "\033esc", "\034^\\", "\034FS", "\034fs", "\035^]", "\035GS", "\035gs", "\036^^", "\036RS", "\036rs", "\037^_", "\037US", "\037us", "\040 ", "\040SP", "\040SPACE", "\040sp", "\040space", "\041!", "\042\"", "\043#", "\044$", "\045%", "\046&", "\047'", "\050(", "\051)", "\052*", "\053+", "\054,", "\055-", "\056.", "\057/", "\0600", "\0611", "\0622", "\0633", "\0644", "\0655", "\0666", "\0677", "\0708", "\0719", "\072:", "\073;", "\074<", "\075=", "\076>", "\077?", "\100@", "\101A", "\102B", "\103C", "\104D", "\105E", "\106F", "\107G", "\110H", "\111I", "\112J", "\113K", "\114L", "\115M", "\116N", "\117O", "\120P", "\121Q", "\122R", "\123S", "\124T", "\125U", "\126V", "\127W", "\130X", "\131Y", "\132Z", "\133[", "\134\\", "\135]", "\136^", "\137_", "\140`", "\141a", "\142b", "\143c", "\144d", "\145e", "\146f", "\147g", "\150h", "\151i", "\152j", "\153k", "\154l", "\155m", "\156n", "\157o", "\160p", "\161q", "\162r", "\163s", "\164t", "\165u", "\166v", "\167w", "\170x", "\171y", "\172z", "\173{"/*}*/, "\174|", /*{*/"\175}", "\176~", "\177^?", "\177DEL", "\177del", "\177DELETE", "\177delete", "\200\\200", "\200^À", "\200x80", "\200X80", "\200\\x80", "\200\\X80", "\201\\201", "\201^Á", "\201x81", "\201X81", "\201\\x81", "\201\\X81", "\202\\202", "\202^Â", "\202x82", "\202X82", "\202\\x82", "\202\\X82", "\203\\203", "\203^Ã", "\203x83", "\203X83", "\203\\x83", "\203\\X83", "\204\\204", "\204^Ä", "\204x84", "\204X84", "\204\\x84", "\204\\X84", "\205\\205", "\205^Å", "\205x85", "\205X85", "\205\\x85", "\205\\X85", "\206\\206", "\206^Æ", "\206x86", "\206X86", "\206\\x86", "\206\\X86", "\207\\207", "\207^Ç", "\207x87", "\207X87", "\207\\x87", "\207\\X87", "\210\\210", "\210^È", "\210x88", "\210X88", "\210\\x88", "\210\\X88", "\211\\211", "\211^É", "\211x89", "\211X89", "\211\\x89", "\211\\X89", "\212\\212", "\212^Ê", "\212x8a", "\212X8a", "\212\\x8a", "\212\\X8a", "\212x8A", "\212X8A", "\212\\x8A", "\212\\X8A", "\213\\213", "\213^Ë", "\213x8b", "\213X8b", "\213\\x8b", "\213\\X8b", "\213x8B", "\213X8B", "\213\\x8B", "\213\\X8B", "\214\\214", "\214^Ì", "\214x8c", "\214X8c", "\214\\x8c", "\214\\X8c", "\214x8C", "\214X8C", "\214\\x8C", "\214\\X8C", "\215\\215", "\215^Í", "\215x8d", "\215X8d", "\215\\x8d", "\215\\X8d", "\215x8D", "\215X8D", "\215\\x8D", "\215\\X8D", "\216\\216", "\216^Î", "\216x8e", "\216X8e", "\216\\x8e", "\216\\X8e", "\216x8E", "\216X8E", "\216\\x8E", "\216\\X8E", "\217\\217", "\217^Ï", "\217x8f", "\217X8f", "\217\\x8f", "\217\\X8f", "\217x8F", "\217X8F", "\217\\x8F", "\217\\X8F", "\220\\220", "\220^Ð", "\220x90", "\220X90", "\220\\x90", "\220\\X90", "\221\\221", "\221^Ñ", "\221x91", "\221X91", "\221\\x91", "\221\\X91", "\222\\222", "\222^Ò", "\222x92", "\222X92", "\222\\x92", "\222\\X92", "\223\\223", "\223^Ó", "\223x93", "\223X93", "\223\\x93", "\223\\X93", "\224\\224", "\224^Ô", "\224x94", "\224X94", "\224\\x94", "\224\\X94", "\225\\225", "\225^Õ", "\225x95", "\225X95", "\225\\x95", "\225\\X95", "\226\\226", "\226^Ö", "\226x96", "\226X96", "\226\\x96", "\226\\X96", "\227\\227", "\227^×", "\227x97", "\227X97", "\227\\x97", "\227\\X97", "\230\\230", "\230^Ø", "\230x98", "\230X98", "\230\\x98", "\230\\X98", "\231\\231", "\231^Ù", "\231x99", "\231X99", "\231\\x99", "\231\\X99", "\232\\232", "\232^Ú", "\232x9a", "\232X9a", "\232\\x9a", "\232\\X9a", "\232x9A", "\232X9A", "\232\\x9A", "\232\\X9A", "\233\\233", "\233^Û", "\233x9b", "\233X9b", "\233\\x9b", "\233\\X9b", "\233x9B", "\233X9B", "\233\\x9B", "\233\\X9B", "\234\\234", "\234^Ü", "\234x9c", "\234X9c", "\234\\x9c", "\234\\X9c", "\234x9C", "\234X9C", "\234\\x9C", "\234\\X9C", "\235\\235", "\235^Ý", "\235x9d", "\235X9d", "\235\\x9d", "\235\\X9d", "\235x9D", "\235X9D", "\235\\x9D", "\235\\X9D", "\236\\236", "\236^Þ", "\236x9e", "\236X9e", "\236\\x9e", "\236\\X9e", "\236x9E", "\236X9E", "\236\\x9E", "\236\\X9E", "\237\\237", "\237^ß", "\237x9f", "\237X9f", "\237\\x9f", "\237\\X9f", "\237x9F", "\237X9F", "\237\\x9F", "\237\\X9F", " \\240", "  ", " xa0", " Xa0", " \\xa0", " \\Xa0", " xA0", " XA0", " \\xA0", " \\XA0", "¡\\241", "¡¡", "¡xa1", "¡Xa1", "¡\\xa1", "¡\\Xa1", "¡xA1", "¡XA1", "¡\\xA1", "¡\\XA1", "¢\\242", "¢¢", "¢xa2", "¢Xa2", "¢\\xa2", "¢\\Xa2", "¢xA2", "¢XA2", "¢\\xA2", "¢\\XA2", "£\\243", "££", "£xa3", "£Xa3", "£\\xa3", "£\\Xa3", "£xA3", "£XA3", "£\\xA3", "£\\XA3", "¤\\244", "¤¤", "¤xa4", "¤Xa4", "¤\\xa4", "¤\\Xa4", "¤xA4", "¤XA4", "¤\\xA4", "¤\\XA4", "¥\\245", "¥¥", "¥xa5", "¥Xa5", "¥\\xa5", "¥\\Xa5", "¥xA5", "¥XA5", "¥\\xA5", "¥\\XA5", "¦\\246", "¦¦", "¦xa6", "¦Xa6", "¦\\xa6", "¦\\Xa6", "¦xA6", "¦XA6", "¦\\xA6", "¦\\XA6", "§\\247", "§§", "§xa7", "§Xa7", "§\\xa7", "§\\Xa7", "§xA7", "§XA7", "§\\xA7", "§\\XA7", "¨\\250", "¨¨", "¨xa8", "¨Xa8", "¨\\xa8", "¨\\Xa8", "¨xA8", "¨XA8", "¨\\xA8", "¨\\XA8", "©\\251", "©©", "©xa9", "©Xa9", "©\\xa9", "©\\Xa9", "©xA9", "©XA9", "©\\xA9", "©\\XA9", "ª\\252", "ªª", "ªxaa", "ªXaa", "ª\\xaa", "ª\\Xaa", "ªxaA", "ªXaA", "ª\\xaA", "ª\\XaA", "ªxAa", "ªXAa", "ª\\xAa", "ª\\XAa", "ªxAA", "ªXAA", "ª\\xAA", "ª\\XAA", "«\\253", "««", "«xab", "«Xab", "«\\xab", "«\\Xab", "«xaB", "«XaB", "«\\xaB", "«\\XaB", "«xAb", "«XAb", "«\\xAb", "«\\XAb", "«xAB", "«XAB", "«\\xAB", "«\\XAB", "¬\\254", "¬¬", "¬xac", "¬Xac", "¬\\xac", "¬\\Xac", "¬xaC", "¬XaC", "¬\\xaC", "¬\\XaC", "¬xAc", "¬XAc", "¬\\xAc", "¬\\XAc", "¬xAC", "¬XAC", "¬\\xAC", "¬\\XAC", "­\\255", "­­", "­xad", "­Xad", "­\\xad", "­\\Xad", "­xaD", "­XaD", "­\\xaD", "­\\XaD", "­xAd", "­XAd", "­\\xAd", "­\\XAd", "­xAD", "­XAD", "­\\xAD", "­\\XAD", "®\\256", "®®", "®xae", "®Xae", "®\\xae", "®\\Xae", "®xaE", "®XaE", "®\\xaE", "®\\XaE", "®xAe", "®XAe", "®\\xAe", "®\\XAe", "®xAE", "®XAE", "®\\xAE", "®\\XAE", "¯\\257", "¯¯", "¯xaf", "¯Xaf", "¯\\xaf", "¯\\Xaf", "¯xaF", "¯XaF", "¯\\xaF", "¯\\XaF", "¯xAf", "¯XAf", "¯\\xAf", "¯\\XAf", "¯xAF", "¯XAF", "¯\\xAF", "¯\\XAF", "°\\260", "°°", "°xb0", "°Xb0", "°\\xb0", "°\\Xb0", "°xB0", "°XB0", "°\\xB0", "°\\XB0", "±\\261", "±±", "±xb1", "±Xb1", "±\\xb1", "±\\Xb1", "±xB1", "±XB1", "±\\xB1", "±\\XB1", "²\\262", "²²", "²xb2", "²Xb2", "²\\xb2", "²\\Xb2", "²xB2", "²XB2", "²\\xB2", "²\\XB2", "³\\263", "³³", "³xb3", "³Xb3", "³\\xb3", "³\\Xb3", "³xB3", "³XB3", "³\\xB3", "³\\XB3", "´\\264", "´´", "´xb4", "´Xb4", "´\\xb4", "´\\Xb4", "´xB4", "´XB4", "´\\xB4", "´\\XB4", "µ\\265", "µµ", "µxb5", "µXb5", "µ\\xb5", "µ\\Xb5", "µxB5", "µXB5", "µ\\xB5", "µ\\XB5", "¶\\266", "¶¶", "¶xb6", "¶Xb6", "¶\\xb6", "¶\\Xb6", "¶xB6", "¶XB6", "¶\\xB6", "¶\\XB6", "·\\267", "··", "·xb7", "·Xb7", "·\\xb7", "·\\Xb7", "·xB7", "·XB7", "·\\xB7", "·\\XB7", "¸\\270", "¸¸", "¸xb8", "¸Xb8", "¸\\xb8", "¸\\Xb8", "¸xB8", "¸XB8", "¸\\xB8", "¸\\XB8", "¹\\271", "¹¹", "¹xb9", "¹Xb9", "¹\\xb9", "¹\\Xb9", "¹xB9", "¹XB9", "¹\\xB9", "¹\\XB9", "º\\272", "ºº", "ºxba", "ºXba", "º\\xba", "º\\Xba", "ºxbA", "ºXbA", "º\\xbA", "º\\XbA", "ºxBa", "ºXBa", "º\\xBa", "º\\XBa", "ºxBA", "ºXBA", "º\\xBA", "º\\XBA", "»\\273", "»»", "»xbb", "»Xbb", "»\\xbb", "»\\Xbb", "»xbB", "»XbB", "»\\xbB", "»\\XbB", "»xBb", "»XBb", "»\\xBb", "»\\XBb", "»xBB", "»XBB", "»\\xBB", "»\\XBB", "¼\\274", "¼¼", "¼xbc", "¼Xbc", "¼\\xbc", "¼\\Xbc", "¼xbC", "¼XbC", "¼\\xbC", "¼\\XbC", "¼xBc", "¼XBc", "¼\\xBc", "¼\\XBc", "¼xBC", "¼XBC", "¼\\xBC", "¼\\XBC", "½\\275", "½½", "½xbd", "½Xbd", "½\\xbd", "½\\Xbd", "½xbD", "½XbD", "½\\xbD", "½\\XbD", "½xBd", "½XBd", "½\\xBd", "½\\XBd", "½xBD", "½XBD", "½\\xBD", "½\\XBD", "¾\\276", "¾¾", "¾xbe", "¾Xbe", "¾\\xbe", "¾\\Xbe", "¾xbE", "¾XbE", "¾\\xbE", "¾\\XbE", "¾xBe", "¾XBe", "¾\\xBe", "¾\\XBe", "¾xBE", "¾XBE", "¾\\xBE", "¾\\XBE", "¿\\277", "¿¿", "¿xbf", "¿Xbf", "¿\\xbf", "¿\\Xbf", "¿xbF", "¿XbF", "¿\\xbF", "¿\\XbF", "¿xBf", "¿XBf", "¿\\xBf", "¿\\XBf", "¿xBF", "¿XBF", "¿\\xBF", "¿\\XBF", "À\\300", "ÀÀ", "Àxc0", "ÀXc0", "À\\xc0", "À\\Xc0", "ÀxC0", "ÀXC0", "À\\xC0", "À\\XC0", "Á\\301", "ÁÁ", "Áxc1", "ÁXc1", "Á\\xc1", "Á\\Xc1", "ÁxC1", "ÁXC1", "Á\\xC1", "Á\\XC1", "Â\\302", "ÂÂ", "Âxc2", "ÂXc2", "Â\\xc2", "Â\\Xc2", "ÂxC2", "ÂXC2", "Â\\xC2", "Â\\XC2", "Ã\\303", "ÃÃ", "Ãxc3", "ÃXc3", "Ã\\xc3", "Ã\\Xc3", "ÃxC3", "ÃXC3", "Ã\\xC3", "Ã\\XC3", "Ä\\304", "ÄÄ", "Äxc4", "ÄXc4", "Ä\\xc4", "Ä\\Xc4", "ÄxC4", "ÄXC4", "Ä\\xC4", "Ä\\XC4", "Å\\305", "ÅÅ", "Åxc5", "ÅXc5", "Å\\xc5", "Å\\Xc5", "ÅxC5", "ÅXC5", "Å\\xC5", "Å\\XC5", "Æ\\306", "ÆÆ", "Æxc6", "ÆXc6", "Æ\\xc6", "Æ\\Xc6", "ÆxC6", "ÆXC6", "Æ\\xC6", "Æ\\XC6", "Ç\\307", "ÇÇ", "Çxc7", "ÇXc7", "Ç\\xc7", "Ç\\Xc7", "ÇxC7", "ÇXC7", "Ç\\xC7", "Ç\\XC7", "È\\310", "ÈÈ", "Èxc8", "ÈXc8", "È\\xc8", "È\\Xc8", "ÈxC8", "ÈXC8", "È\\xC8", "È\\XC8", "É\\311", "ÉÉ", "Éxc9", "ÉXc9", "É\\xc9", "É\\Xc9", "ÉxC9", "ÉXC9", "É\\xC9", "É\\XC9", "Ê\\312", "ÊÊ", "Êxca", "ÊXca", "Ê\\xca", "Ê\\Xca", "ÊxcA", "ÊXcA", "Ê\\xcA", "Ê\\XcA", "ÊxCa", "ÊXCa", "Ê\\xCa", "Ê\\XCa", "ÊxCA", "ÊXCA", "Ê\\xCA", "Ê\\XCA", "Ë\\313", "ËË", "Ëxcb", "ËXcb", "Ë\\xcb", "Ë\\Xcb", "ËxcB", "ËXcB", "Ë\\xcB", "Ë\\XcB", "ËxCb", "ËXCb", "Ë\\xCb", "Ë\\XCb", "ËxCB", "ËXCB", "Ë\\xCB", "Ë\\XCB", "Ì\\314", "ÌÌ", "Ìxcc", "ÌXcc", "Ì\\xcc", "Ì\\Xcc", "ÌxcC", "ÌXcC", "Ì\\xcC", "Ì\\XcC", "ÌxCc", "ÌXCc", "Ì\\xCc", "Ì\\XCc", "ÌxCC", "ÌXCC", "Ì\\xCC", "Ì\\XCC", "Í\\315", "ÍÍ", "Íxcd", "ÍXcd", "Í\\xcd", "Í\\Xcd", "ÍxcD", "ÍXcD", "Í\\xcD", "Í\\XcD", "ÍxCd", "ÍXCd", "Í\\xCd", "Í\\XCd", "ÍxCD", "ÍXCD", "Í\\xCD", "Í\\XCD", "Î\\316", "ÎÎ", "Îxce", "ÎXce", "Î\\xce", "Î\\Xce", "ÎxcE", "ÎXcE", "Î\\xcE", "Î\\XcE", "ÎxCe", "ÎXCe", "Î\\xCe", "Î\\XCe", "ÎxCE", "ÎXCE", "Î\\xCE", "Î\\XCE", "Ï\\317", "ÏÏ", "Ïxcf", "ÏXcf", "Ï\\xcf", "Ï\\Xcf", "ÏxcF", "ÏXcF", "Ï\\xcF", "Ï\\XcF", "ÏxCf", "ÏXCf", "Ï\\xCf", "Ï\\XCf", "ÏxCF", "ÏXCF", "Ï\\xCF", "Ï\\XCF", "Ð\\320", "ÐÐ", "Ðxd0", "ÐXd0", "Ð\\xd0", "Ð\\Xd0", "ÐxD0", "ÐXD0", "Ð\\xD0", "Ð\\XD0", "Ñ\\321", "ÑÑ", "Ñxd1", "ÑXd1", "Ñ\\xd1", "Ñ\\Xd1", "ÑxD1", "ÑXD1", "Ñ\\xD1", "Ñ\\XD1", "Ò\\322", "ÒÒ", "Òxd2", "ÒXd2", "Ò\\xd2", "Ò\\Xd2", "ÒxD2", "ÒXD2", "Ò\\xD2", "Ò\\XD2", "Ó\\323", "ÓÓ", "Óxd3", "ÓXd3", "Ó\\xd3", "Ó\\Xd3", "ÓxD3", "ÓXD3", "Ó\\xD3", "Ó\\XD3", "Ô\\324", "ÔÔ", "Ôxd4", "ÔXd4", "Ô\\xd4", "Ô\\Xd4", "ÔxD4", "ÔXD4", "Ô\\xD4", "Ô\\XD4", "Õ\\325", "ÕÕ", "Õxd5", "ÕXd5", "Õ\\xd5", "Õ\\Xd5", "ÕxD5", "ÕXD5", "Õ\\xD5", "Õ\\XD5", "Ö\\326", "ÖÖ", "Öxd6", "ÖXd6", "Ö\\xd6", "Ö\\Xd6", "ÖxD6", "ÖXD6", "Ö\\xD6", "Ö\\XD6", "×\\327", "××", "×xd7", "×Xd7", "×\\xd7", "×\\Xd7", "×xD7", "×XD7", "×\\xD7", "×\\XD7", "Ø\\330", "ØØ", "Øxd8", "ØXd8", "Ø\\xd8", "Ø\\Xd8", "ØxD8", "ØXD8", "Ø\\xD8", "Ø\\XD8", "Ù\\331", "ÙÙ", "Ùxd9", "ÙXd9", "Ù\\xd9", "Ù\\Xd9", "ÙxD9", "ÙXD9", "Ù\\xD9", "Ù\\XD9", "Ú\\332", "ÚÚ", "Úxda", "ÚXda", "Ú\\xda", "Ú\\Xda", "ÚxdA", "ÚXdA", "Ú\\xdA", "Ú\\XdA", "ÚxDa", "ÚXDa", "Ú\\xDa", "Ú\\XDa", "ÚxDA", "ÚXDA", "Ú\\xDA", "Ú\\XDA", "Û\\333", "ÛÛ", "Ûxdb", "ÛXdb", "Û\\xdb", "Û\\Xdb", "ÛxdB", "ÛXdB", "Û\\xdB", "Û\\XdB", "ÛxDb", "ÛXDb", "Û\\xDb", "Û\\XDb", "ÛxDB", "ÛXDB", "Û\\xDB", "Û\\XDB", "Ü\\334", "ÜÜ", "Üxdc", "ÜXdc", "Ü\\xdc", "Ü\\Xdc", "ÜxdC", "ÜXdC", "Ü\\xdC", "Ü\\XdC", "ÜxDc", "ÜXDc", "Ü\\xDc", "Ü\\XDc", "ÜxDC", "ÜXDC", "Ü\\xDC", "Ü\\XDC", "Ý\\335", "ÝÝ", "Ýxdd", "ÝXdd", "Ý\\xdd", "Ý\\Xdd", "ÝxdD", "ÝXdD", "Ý\\xdD", "Ý\\XdD", "ÝxDd", "ÝXDd", "Ý\\xDd", "Ý\\XDd", "ÝxDD", "ÝXDD", "Ý\\xDD", "Ý\\XDD", "Þ\\336", "ÞÞ", "Þxde", "ÞXde", "Þ\\xde", "Þ\\Xde", "ÞxdE", "ÞXdE", "Þ\\xdE", "Þ\\XdE", "ÞxDe", "ÞXDe", "Þ\\xDe", "Þ\\XDe", "ÞxDE", "ÞXDE", "Þ\\xDE", "Þ\\XDE", "ß\\337", "ßß", "ßxdf", "ßXdf", "ß\\xdf", "ß\\Xdf", "ßxdF", "ßXdF", "ß\\xdF", "ß\\XdF", "ßxDf", "ßXDf", "ß\\xDf", "ß\\XDf", "ßxDF", "ßXDF", "ß\\xDF", "ß\\XDF", "à\\340", "àà", "àxe0", "àXe0", "à\\xe0", "à\\Xe0", "àxE0", "àXE0", "à\\xE0", "à\\XE0", "á\\341", "áá", "áxe1", "áXe1", "á\\xe1", "á\\Xe1", "áxE1", "áXE1", "á\\xE1", "á\\XE1", "â\\342", "ââ", "âxe2", "âXe2", "â\\xe2", "â\\Xe2", "âxE2", "âXE2", "â\\xE2", "â\\XE2", "ã\\343", "ãã", "ãxe3", "ãXe3", "ã\\xe3", "ã\\Xe3", "ãxE3", "ãXE3", "ã\\xE3", "ã\\XE3", "ä\\344", "ää", "äxe4", "äXe4", "ä\\xe4", "ä\\Xe4", "äxE4", "äXE4", "ä\\xE4", "ä\\XE4", "å\\345", "åå", "åxe5", "åXe5", "å\\xe5", "å\\Xe5", "åxE5", "åXE5", "å\\xE5", "å\\XE5", "æ\\346", "ææ", "æxe6", "æXe6", "æ\\xe6", "æ\\Xe6", "æxE6", "æXE6", "æ\\xE6", "æ\\XE6", "ç\\347", "çç", "çxe7", "çXe7", "ç\\xe7", "ç\\Xe7", "çxE7", "çXE7", "ç\\xE7", "ç\\XE7", "è\\350", "èè", "èxe8", "èXe8", "è\\xe8", "è\\Xe8", "èxE8", "èXE8", "è\\xE8", "è\\XE8", "é\\351", "éé", "éxe9", "éXe9", "é\\xe9", "é\\Xe9", "éxE9", "éXE9", "é\\xE9", "é\\XE9", "ê\\352", "êê", "êxea", "êXea", "ê\\xea", "ê\\Xea", "êxeA", "êXeA", "ê\\xeA", "ê\\XeA", "êxEa", "êXEa", "ê\\xEa", "ê\\XEa", "êxEA", "êXEA", "ê\\xEA", "ê\\XEA", "ë\\353", "ëë", "ëxeb", "ëXeb", "ë\\xeb", "ë\\Xeb", "ëxeB", "ëXeB", "ë\\xeB", "ë\\XeB", "ëxEb", "ëXEb", "ë\\xEb", "ë\\XEb", "ëxEB", "ëXEB", "ë\\xEB", "ë\\XEB", "ì\\354", "ìì", "ìxec", "ìXec", "ì\\xec", "ì\\Xec", "ìxeC", "ìXeC", "ì\\xeC", "ì\\XeC", "ìxEc", "ìXEc", "ì\\xEc", "ì\\XEc", "ìxEC", "ìXEC", "ì\\xEC", "ì\\XEC", "í\\355", "íí", "íxed", "íXed", "í\\xed", "í\\Xed", "íxeD", "íXeD", "í\\xeD", "í\\XeD", "íxEd", "íXEd", "í\\xEd", "í\\XEd", "íxED", "íXED", "í\\xED", "í\\XED", "î\\356", "îî", "îxee", "îXee", "î\\xee", "î\\Xee", "îxeE", "îXeE", "î\\xeE", "î\\XeE", "îxEe", "îXEe", "î\\xEe", "î\\XEe", "îxEE", "îXEE", "î\\xEE", "î\\XEE", "ï\\357", "ïï", "ïxef", "ïXef", "ï\\xef", "ï\\Xef", "ïxeF", "ïXeF", "ï\\xeF", "ï\\XeF", "ïxEf", "ïXEf", "ï\\xEf", "ï\\XEf", "ïxEF", "ïXEF", "ï\\xEF", "ï\\XEF", "ð\\360", "ðð", "ðxf0", "ðXf0", "ð\\xf0", "ð\\Xf0", "ðxF0", "ðXF0", "ð\\xF0", "ð\\XF0", "ñ\\361", "ññ", "ñxf1", "ñXf1", "ñ\\xf1", "ñ\\Xf1", "ñxF1", "ñXF1", "ñ\\xF1", "ñ\\XF1", "ò\\362", "òò", "òxf2", "òXf2", "ò\\xf2", "ò\\Xf2", "òxF2", "òXF2", "ò\\xF2", "ò\\XF2", "ó\\363", "óó", "óxf3", "óXf3", "ó\\xf3", "ó\\Xf3", "óxF3", "óXF3", "ó\\xF3", "ó\\XF3", "ô\\364", "ôô", "ôxf4", "ôXf4", "ô\\xf4", "ô\\Xf4", "ôxF4", "ôXF4", "ô\\xF4", "ô\\XF4", "õ\\365", "õõ", "õxf5", "õXf5", "õ\\xf5", "õ\\Xf5", "õxF5", "õXF5", "õ\\xF5", "õ\\XF5", "ö\\366", "öö", "öxf6", "öXf6", "ö\\xf6", "ö\\Xf6", "öxF6", "öXF6", "ö\\xF6", "ö\\XF6", "÷\\367", "÷÷", "÷xf7", "÷Xf7", "÷\\xf7", "÷\\Xf7", "÷xF7", "÷XF7", "÷\\xF7", "÷\\XF7", "ø\\370", "øø", "øxf8", "øXf8", "ø\\xf8", "ø\\Xf8", "øxF8", "øXF8", "ø\\xF8", "ø\\XF8", "ù\\371", "ùù", "ùxf9", "ùXf9", "ù\\xf9", "ù\\Xf9", "ùxF9", "ùXF9", "ù\\xF9", "ù\\XF9", "ú\\372", "úú", "úxfa", "úXfa", "ú\\xfa", "ú\\Xfa", "úxfA", "úXfA", "ú\\xfA", "ú\\XfA", "úxFa", "úXFa", "ú\\xFa", "ú\\XFa", "úxFA", "úXFA", "ú\\xFA", "ú\\XFA", "û\\373", "ûû", "ûxfb", "ûXfb", "û\\xfb", "û\\Xfb", "ûxfB", "ûXfB", "û\\xfB", "û\\XfB", "ûxFb", "ûXFb", "û\\xFb", "û\\XFb", "ûxFB", "ûXFB", "û\\xFB", "û\\XFB", "ü\\374", "üü", "üxfc", "üXfc", "ü\\xfc", "ü\\Xfc", "üxfC", "üXfC", "ü\\xfC", "ü\\XfC", "üxFc", "üXFc", "ü\\xFc", "ü\\XFc", "üxFC", "üXFC", "ü\\xFC", "ü\\XFC", "ý\\375", "ýý", "ýxfd", "ýXfd", "ý\\xfd", "ý\\Xfd", "ýxfD", "ýXfD", "ý\\xfD", "ý\\XfD", "ýxFd", "ýXFd", "ý\\xFd", "ý\\XFd", "ýxFD", "ýXFD", "ý\\xFD", "ý\\XFD", "þ\\376", "þþ", "þxfe", "þXfe", "þ\\xfe", "þ\\Xfe", "þxfE", "þXfE", "þ\\xfE", "þ\\XfE", "þxFe", "þXFe", "þ\\xFe", "þ\\XFe", "þxFE", "þXFE", "þ\\xFE", "þ\\XFE", "ÿ\\377", "ÿÿ", "ÿxff", "ÿXff", "ÿ\\xff", "ÿ\\Xff", "ÿxfF", "ÿXfF", "ÿ\\xfF", "ÿ\\XfF", "ÿxFf", "ÿXFf", "ÿ\\xFf", "ÿ\\XFf", "ÿxFF", "ÿXFF", "ÿ\\xFF", "ÿ\\XFF" }; #define NCHARNAMES (sizeof(charnames)/sizeof(charnames[0])) static int maxcnlen = 0; static void kf_backward_character(void); static void kf_backward_word(void); static void kf_beginning_of_line(void); static void kf_case_word_capitalize(void); static void kf_case_word_lower(void); static void kf_case_word_upper(void); static void kf_complete(void); static void kf_default(void); static void kf_delete_next_character(void); static void kf_delete_next_word(void); static void kf_delete_previous_character(void); static void kf_delete_previous_word(void); static void kf_end_of_line(void); static void kf_finish_line(void); static void kf_forward_character(void); static void kf_forward_word(void); static void kf_history_n(void); static void kf_history_p(void); static void kf_interrupt(void); static void kf_kill_entire_line(void); static void kf_kill_region(void); static void kf_kill_to_end_of_line(void); static void kf_literal_next(void); static void kf_noop(void); static void kf_number_dec(void); static void kf_number_inc(void); static void kf_redraw_line(void); static void kf_ring_yank(void); static void kf_self_insert(void); static void kf_set_mark(void); static void kf_show_completions(void); static void kf_tcsh_history_n(void); static void kf_tcsh_history_p(void); static void kf_transpose_after(void); static void kf_transpose_around(void); static void kf_transpose_before(void); static void kf_yank(void); static const PRIMINIT prims[] = { { "backward-character", &kf_backward_character }, { "backward-word", &kf_backward_word }, { "beginning-of-line", &kf_beginning_of_line }, { "case-word-capitalize", &kf_case_word_capitalize }, { "case-word-lower", &kf_case_word_lower }, { "case-word-upper", &kf_case_word_upper }, { "complete", &kf_complete }, { "default", &kf_default }, { "delete-next-character", &kf_delete_next_character }, { "delete-next-word", &kf_delete_next_word }, { "delete-previous-character", &kf_delete_previous_character }, { "delete-previous-word", &kf_delete_previous_word }, { "end-of-line", &kf_end_of_line }, { "finish-line", &kf_finish_line }, { "forward-character", &kf_forward_character }, { "forward-word", &kf_forward_word }, { "history-^N", &kf_history_n }, { "history-^P", &kf_history_p }, { "interrupt", &kf_interrupt }, { "kill-entire-line", &kf_kill_entire_line }, { "kill-region", &kf_kill_region }, { "kill-to-end-of-line", &kf_kill_to_end_of_line }, { "literal-next", &kf_literal_next }, { "noop", &kf_noop }, { "number-dec", &kf_number_dec }, { "number-inc", &kf_number_inc }, { "redraw-line", &kf_redraw_line }, { "ring-yank", &kf_ring_yank }, { "self-insert", &kf_self_insert }, { "set-mark", &kf_set_mark }, { "show-completions", &kf_show_completions }, { "tcsh-history-n", &kf_tcsh_history_n }, { "tcsh-history-p", &kf_tcsh_history_p }, { "transpose-after", &kf_transpose_after }, { "transpose-around", &kf_transpose_around }, { "transpose-before", &kf_transpose_before }, { "yank", &kf_yank }, { 0 } }; static const DEFBIND bindings[] = { { "^@", "set-mark" }, { "^A", "beginning-of-line" }, { "^B", "backward-character" }, { "^C", "interrupt" }, { "^D", "delete-next-character" }, { "^E", "end-of-line" }, { "^F", "forward-character" }, { "^H", "delete-previous-character" }, { "^I", "self-insert" }, { "^J", "finish-line" }, { "^K", "kill-to-end-of-line" }, { "^L", "redraw-line" }, { "^M", "finish-line" }, { "^N", "history-^N" }, { "^P", "history-^P" }, { "^T", "transpose-before" }, { "^U", "kill-entire-line" }, { "^V", "literal-next" }, { "^W", "kill-region" }, { "^X", "kill-entire-line" }, { "^Y", "yank" }, { "DEL", "delete-previous-character" }, { "ESC-ESC", "complete" }, { "ESC-*", "show-completions" }, { "ESC-+", "number-inc" }, { "ESC--", "number-dec" }, { "ESC-=", "number-inc" }, { "ESC-[-A", "history-^P" }, { "ESC-[-B", "history-^N" }, { "ESC-[-C", "forward-character" }, { "ESC-[-D", "backward-character" }, { "ESC-6", "case-word-capitalize" }, { "ESC-b", "backward-word" }, { "ESC-d", "delete-next-word" }, { "ESC-f", "forward-word" }, { "ESC-h", "delete-previous-word" }, { "ESC-l", "case-word-lower" }, { "ESC-n", "tcsh-history-n" }, { "ESC-p", "tcsh-history-p" }, { "ESC-u", "case-word-upper" }, { "ESC-y", "ring-yank" }, { 0 } }; static FILE *shof; static int bifd_i; static int bifd_o; static int bifd_e; static FILE *bif_i; static FILE *bif_o; static FILE *bif_e; static KEYFUNC *keyfuncs; static KEYMAP rootmap; static KEYMAP *curkeymap; static SHELLSTATE *state; static SHELLSTATE *cwdstate; static JOB *jobhead; static JOB *jobtail; static PT *bqhead; static PT *bqtail; static volatile int sigchld_wpipe; static int sigchld_rpipe; static sigset_t sigchld_set; static pid_t mypg; static unsigned char *ibuf = 0; static int iballoc = 0; static int iblen; static int ibcurs; static char *iwbuf = 0; static int iwalloc = 0; static int iwlen; static int iwcurs; static int iredraw; static char *icbuf = 0; static int icalloc = 0; static int iclen; static int iccurs; static int ibdirty; static int iplen; static int ibdone; static unsigned char ilastkey; static int imark = -1; static int itempmark; static unsigned int itempmarkseq; static unsigned int ikeyseq = 2; static char *iline; static int ilen; static HISTLIST histroot; static int histsize = 1000; /* XXX */ static HISTLIST killring; static int killsize = 10; /* XXX */ static unsigned int history_np_seq = 0; static HISTENT *history_np_h; static unsigned int yankseq; static HISTENT *yank_h; static char *shfdv; static int nshfd; static TOKEN **tokens; static int ntokens; static int atokens; static int attoken; static PT *cmdline; #define REDIR_ORDER_LTOR 1 #define REDIR_ORDER_RTOL 2 static int redir_order = REDIR_ORDER_LTOR; static const EXITSTAT es_0 = { .type=ET_EXIT, .u = { .exit = 0 } }; static const EXITSTAT es_1 = { .type=ET_EXIT, .u = { .exit = 1 } }; static void vshp(const char *, va_list) __attribute__((__format__(__printf__,1,0))); static void vshp(const char *fmt, va_list ap) { vfprintf(shof,fmt,ap); } static void shp(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void shp(const char *fmt, ...) { va_list ap; va_start(ap,fmt); vshp(fmt,ap); va_end(ap); } #define panic(fmt, rest...) panic_(__FILE__,__LINE__,__FUNCTION__,fmt , ## rest) static void panic_(const char *, int, const char *, const char *, ...) __attribute__((__format__(__printf__,4,5),__noreturn__)); static void panic_(const char *file, int line, const char *fxn, const char *fmt, ...) { va_list ap; fflush(0); fflush(0); fprintf(shof,"panic (%s, %s line %d): ",fxn,file,line); va_start(ap,fmt); vfprintf(shof,fmt,ap); va_end(ap); fprintf(shof,"\n"); fprintf(stderr,"panic (%s, %s line %d): ",fxn,file,line); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,"\n"); fflush(0); fflush(0); abort(); } #define abort YYY@YYY #ifdef MALDEBUG typedef struct xhdr XHDR; typedef union xhdru XHDRU; struct xhdr { unsigned long int magic1; #define XH_MAGIC1 0x93d7dcc5 XHDR *flink; XHDR *blink; int size; const char *file; int line; const char *fxn; unsigned long int magic2; #define XH_MAGIC2 0xd8bcf7b3 } ; union xhdru { XHDR xh; } __attribute__((__aligned__)); static XHDR *xroot; #define xget(nb) xget_(nb,__FILE__,__LINE__,__FUNCTION__) static void *xget_(unsigned int nb, const char *file, int line, const char *fxn) { XHDRU *mem; XHDR *xh; mem = malloc(nb+sizeof(XHDRU)); xh = &mem->xh; xh->magic1 = XH_MAGIC1; xh->flink = xroot; xh->blink = 0; if (xroot) xroot->blink = xh; xroot = xh; xh->size = nb; xh->file = file; xh->line = line; xh->fxn = fxn; xh->magic2 = XH_MAGIC2; memset(mem+1,65,nb); return(mem+1); } static void xput(void *old) { XHDRU *mem; XHDR *xh; if (old == 0) return; mem = ((XHDRU *)old) - 1; xh = &mem->xh; if (xh->magic1 != XH_MAGIC1) panic("bad magic number 1"); if (xh->magic2 != XH_MAGIC2) panic("bad magic number 2"); if (xh->flink) xh->flink->blink = xh->blink; if (xh->blink) xh->blink->flink = xh->flink; else xroot = xh->flink; memset(mem,67,xh->size+sizeof(XHDRU)); free(mem); } #define xreget(blk,nb) xreget_(blk,nb,__FILE__,__LINE__,__FUNCTION__) static void *xreget_(void *old, unsigned int nb, const char *file, int line, const char *fxn) { XHDRU *omem; XHDRU *nmem; XHDR *xh; XHDR *ofxh; XHDR *obxh; if (old == 0) return(xget_(nb,file,line,fxn)); if (nb < 1) { xput(old); return(0); } omem = ((XHDRU *)old) - 1; xh = &omem->xh; if (xh->magic1 != XH_MAGIC1) panic("bad magic number 1"); if (xh->magic2 != XH_MAGIC2) panic("bad magic number 2"); xh->file = file; xh->line = line; xh->fxn = fxn; if (nb == xh->size) return(old); if (nb < xh->size) { memset(nb+(char *)old,69,xh->size-nb); xh->size = nb; return(old); } ofxh = xh->flink; obxh = xh->blink; nmem = malloc(nb+sizeof(XHDRU)); bcopy(omem,nmem,xh->size+sizeof(XHDRU)); memset(omem,71,xh->size+sizeof(XHDRU)); free(omem); xh = &nmem->xh; if (ofxh) ofxh->blink = xh; if (obxh) obxh->flink = xh; else xroot = xh; memset(xh->size+(char *)(nmem+1),73,nb-xh->size); xh->size = nb; return(nmem+1); } static void xdump(void) __attribute__((__unused__)); static void xdump(void) { XHDR *xh; for (xh=xroot;xh;xh=xh->flink) { if (xh->magic1 != XH_MAGIC1) panic("bad magic number 1"); if (xh->magic2 != XH_MAGIC2) panic("bad magic number 2"); shp("\"%s\", line %d: %s(): %d\n",xh->file,xh->line,xh->fxn,xh->size); } } static char *xaprintf(const char *, ...) __attribute__((__format__(__printf__,1,2))); static char *xaprintf(const char *fmt, ...) { va_list ap; FILE *f; char *buf; int len; static int w(void *cookie __attribute__((__unused__)), const char *b, int l) { buf = xreget(buf,len+l+1); bcopy(b,buf+len,l); len += l; buf[len] = '\0'; return(l); } len = 0; buf = xget(1); buf[0] = '\0'; f = fwopen(0,&w); va_start(ap,fmt); vfprintf(f,fmt,ap); va_end(ap); fclose(f); return(buf); } #define asprintf(x...) YYY@YYY #define malloc(x) YYY@YYY #define realloc(x,y) YYY@YYY #define free(x) YYY@YYY #else #define xget(n) malloc(n) #define xput(x) free(x) #define xreget(x,n) realloc(x,n) #define xdump() #endif static char *xstrdup(const char *s, int l) { char *t; if (s == 0) return(0); if (l < 0) l = strlen(s); t = xget(l+1); bcopy(s,t,l); t[l] = '\0'; return(t); } #define strdup(x) YYY@YYY static int bif__r(void *fdpv, char *buf, int len) { return(read(*(int *)fdpv,buf,len)); } static int bif__w(void *fdpv, const char *buf, int len) { return(write(*(int *)fdpv,buf,len)); } static void setup_files(void) { int fd; fd = getdtablesize() - 1; dup2(1,fd); fcntl(fd,F_SETFD,1); shof = fdopen(fd,"w"); setlinebuf(shof); bif_i = fropen(&bifd_i,&bif__r); bif_o = fwopen(&bifd_o,&bif__w); bif_e = fwopen(&bifd_e,&bif__w); shfdv = 0; nshfd = 0; } static void add_builtin(const char *name, void (*fn)(void)) { KEYFUNC *kf; kf = xget(sizeof(KEYFUNC)); kf->name = name; kf->type = KFT_BUILTIN; kf->u.builtin = fn; kf->link = keyfuncs; keyfuncs = kf; } static int lookup_bind(const char *name, KEY *k) { KEYFUNC *kf; for (kf=keyfuncs;kf;kf=kf->link) { if (!strcmp(kf->name,name)) { k->type = KT_LEAF; k->u.leaf = kf; return(0); } } fprintf(stderr,"%s: unknown function name\n",name); return(1); } static void sort_charnames(void) { const char *tmp[NCHARNAMES]; static void sort(int base, int len) { int l1; int l2; int p1; int p2; const char **tp; if (len < 2) return; l1 = len >> 1; l2 = len - l1; sort(base,l1); sort(base+l1,l2); p1 = base; p2 = base + l1; tp = &tmp[0]; while (l1 && l2) { if (strcmp(charnames[p1]+1,charnames[p2]+1) < 0) { *tp++ = charnames[p1++]; l1 --; } else { *tp++ = charnames[p2++]; l2 --; } } if (l1) bcopy(charnames+p1,charnames+base+len-l1,l1*sizeof(*charnames)); bcopy(&tmp[0],charnames+base,(tp-&tmp[0])*sizeof(*charnames)); } sort(0,NCHARNAMES); } static int parse_keyseq(const char *seq, unsigned char **vp, int *lp) { unsigned char *buf; int len; int i; int j; int k; int l; int m; int c; if (! seq[0]) return(1); if (maxcnlen == 0) { sort_charnames(); maxcnlen = strlen(charnames[NCHARNAMES-1]+1); for (i=NCHARNAMES-2;i>=0;i--) { l = strlen(charnames[i]); if (l > maxcnlen) maxcnlen = l; } } buf = 0; len = 0; i = 0; while (1) { for (j=i+1;seq[j]&&(seq[j]!='-');j++) ; if (seq[j] && !seq[j+1]) break; l = j - i; j = -1; k = NCHARNAMES; while (k-j > 1) { m = (k + j) >> 1; c = strncmp(seq+i,charnames[m]+1,l); if ((c == 0) && charnames[m][l+1]) c = -1; if (c <= 0) k = m; if (c >= 0) j = m; } if (j != k) break; buf = xreget(buf,len+1); buf[len++] = charnames[j][0]; i += l; if (! seq[i]) { *vp = buf; *lp = len; return(0); } i ++; } xput(buf); return(1); } static void free_keymap_recursive(KEYMAP *m) { int i; for (i=0;i<256;i++) { if (m->key[i].type == KT_MAP) free_keymap_recursive(m->key[i].u.map); } xput(m); } static int bind_sequence(const unsigned char *seq, int seqlen, const char *to) { KEY new; KEYMAP *m; KEY *k; int i; if (lookup_bind(to,&new)) return(1); m = &rootmap; for (;seqlen>1;seqlen--) { k = &m->key[*seq++]; if (k->type != KT_MAP) { k->type = KT_MAP; k->u.map = xget(sizeof(KEYMAP)); for (i=0;i<256;i++) k->u.map->key[i].type = KT_UNBOUND; } m = k->u.map; } k = &m->key[*seq]; if (k->type == KT_MAP) free_keymap_recursive(k->u.map); *k = new; return(0); } static void setup_maps(void) { int i; unsigned char *s; int l; KEY k; static void lose(void) { fprintf(stderr,"%s: error in editor startup\n",__progname); exit(1); } keyfuncs = 0; for (i=0;prims[i].name;i++) add_builtin(prims[i].name,prims[i].fxn); if (lookup_bind("default",&k)) lose(); for (i=0;i<256;i++) rootmap.key[i] = k; for (i=0;bindings[i].key;i++) { if (parse_keyseq(bindings[i].key,&s,&l)) { fprintf(stderr,"%s: bad key sequence in startup: %s\n",__progname,bindings[i].key); exit(1); } if (bind_sequence(s,l,bindings[i].binding)) lose(); xput(s); } } static void setup_history(HISTLIST *list, int *sizep) { list->root = xget(sizeof(HISTENT)); list->sizep = sizep; list->root->older = list->root; list->root->newer = list->root; list->root->txt = 0; list->root->len = -1; } static CWD *cwd_new(int fd) { CWD *c; c = xget(sizeof(CWD)); c->refcnt = 1; c->fd = fd; return(c); } static ENVIRONMENT *setup_environment(char **env) { ENVIRONMENT *e; int i; e = xget(sizeof(ENVIRONMENT)); e->refcnt = 1; for (i=0;env[i];i++) ; e->nvars = i; e->nvalloc = i + 8; e->vars = xget(e->nvalloc*sizeof(char *)); for (i=0;env[i];i++) e->vars[i] = xstrdup(env[i],-1); e->vars[i] = 0; return(e); } static PATH *path_from_str(const char *s) { PATH *p; int len; p = xget(sizeof(PATH)); p->refcnt = 1; if (s == 0) { p->nstrs = 0; p->strs = 0; p->strbuf = 0; p->maxlen = 0; } else { int i; int n; int in; n = 1; for (i=0;s[i];i++) if (s[i] == ':') n ++; p->nstrs = n; p->strs = xget(n*sizeof(char *)); p->strbuf = xget(i+1); p->maxlen = 0; in = 0; n = 0; for (i=0;s[i];i++) { if (! in) { p->strs[n++] = p->strbuf + i; len = 0; in = 1; } if (s[i] == ':') { p->strbuf[i] = '\0'; in = 0; } else { p->strbuf[i] = s[i]; len ++; if (len > p->maxlen) p->maxlen = len; } } p->strbuf[i] = '\0'; } return(p); } static char *env_ptr(const char *var) { ENVIRONMENT *e; int i; int l; l = strlen(var); e = state->env; for (i=e->nvars-1;i>=0;i--) { if (!bcmp(e->vars[i],var,l) && (e->vars[i][l] == '=')) { return(e->vars[i]+l+1); } } return(0); } static void shfd_add(int fd) { fcntl(fd,F_SETFD,1); if (fd >= nshfd) { shfdv = xreget(shfdv,fd+1); bzero(shfdv+nshfd,fd+1-nshfd); nshfd = fd + 1; } if (shfdv[fd]) panic("duplicate shfd_add"); shfdv[fd] = 1; } static void shfd_close(int fd) { if (fd < 0) return; if ((fd >= nshfd) || !shfdv[fd]) panic("bad shfd_close"); close(fd); shfdv[fd] = 0; } static void shfd_closeall(void) { int i; for (i=nshfd-1;i>=0;i--) if (shfdv[i]) close(i); } static void shellstate_init(char **env) { int fd; fd = open(".",O_NOACCESS,0); if (fd < 0) { fprintf(stderr,"%s: can't open .: %s\n",__progname,strerror(errno)); exit(1); } shp("[got cwd fd %d]\n",fd); shfd_add(fd); state = xget(sizeof(SHELLSTATE)); state->debug = 1; state->cwd = cwd_new(fd); state->env = setup_environment(env); state->path = path_from_str(env_ptr("PATH")); state->charset = &charset_iso_8859_1; cwdstate = state; } static void job_link_init(void) { jobhead = 0; jobtail = 0; } static void sigchld_handler(int sig __attribute__((__unused__))) { int i; i = sigchld_wpipe; if (i >= 0) { shfd_close(i); sigchld_wpipe = -1; } } static void make_sigchld_pipe(void) { int p[2]; while (pipe(&p[0]) < 0) { shp("pipe: %s\n",strerror(errno)); sleep(1); } shp("[sigchld pipe %d %d]\n",p[0],p[1]); shfd_add(p[0]); shfd_add(p[1]); sigchld_rpipe = p[0]; sigchld_wpipe = p[1]; } static void reset_sigchld_pipe(void) { sigset_t s; sigemptyset(&s); /* necessary per manpage :-þ */ sigprocmask(SIG_BLOCK,&sigchld_set,&s); shfd_close(sigchld_wpipe); shfd_close(sigchld_rpipe); make_sigchld_pipe(); sigprocmask(SIG_SETMASK,&s,0); } static void setup_signals(void) { struct sigaction sa; make_sigchld_pipe(); sigemptyset(&sigchld_set); sigaddset(&sigchld_set,SIGCHLD); sa.sa_handler = &sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGCHLD,&sa,0); sigprocmask(SIG_UNBLOCK,&sigchld_set,0); } static void taketty(void) { tcsetpgrp(0,mypg); } static void cwdsync(void) { if (cwdstate != state) { fchdir(state->cwd->fd); cwdstate = state; } } static void ibgrow(int l) { if (l >= iballoc) ibuf = xreget(ibuf,(iballoc=l+64)+1); } static void update_line(void) { int i; int c; int n; static void wchar(int c) { if (iwlen >= iwalloc) iwbuf = xreget(iwbuf,iwalloc=iwlen+64); iwbuf[iwlen++] = c; } if (iredraw) { iclen = 0; iccurs = 0; } iwlen = 0; for (i=0;i>3)&7)+'0'); wchar((c&7)+'0'); } else { wchar(c); } } if (i == ibcurs) iwcurs = iwlen; if (icalloc < iwlen) icbuf = xreget(icbuf,icalloc=iwlen+64); for (i=0;(i i) { putc('\b',shof); iccurs --; } while (iccurs < i) { putc(icbuf[iccurs++],shof); } if (iwlen == iclen) { n = iwlen - 1; while (iwbuf[n] == icbuf[n]) n --; fwrite(iwbuf+i,1,(n-i)+1,shof); bcopy(iwbuf+i,icbuf+i,(n-i)+1); iccurs = n + 1; } else { fwrite(iwbuf+i,1,iwlen-i,shof); bcopy(iwbuf+i,icbuf+i,iwlen-i); iccurs = iwlen; while (iccurs < iclen) { putc(' ',shof); iccurs ++; } iclen = iwlen; } while (iccurs > iwcurs) { putc('\b',shof); iccurs --; } while (iccurs < iwcurs) { putc(icbuf[iccurs],shof); iccurs ++; } } else if (iccurs != iwcurs) { while (iccurs > iwcurs) { putc('\b',shof); iccurs --; } while (iccurs < iwcurs) { putc(iwbuf[iccurs++],shof); } } ibdirty = 0; iredraw = 0; } static void beep(void) { putc('\7',shof); } static int ct_wordchar(unsigned char c) { return(state->charset->flags[c]&CTF_WORDCHAR); } static int ct_printable(unsigned char c) { return(state->charset->flags[c]&CTF_PRINTABLE); } static int ct_digit(unsigned char c) { return((c>='0')&&(c<='9')); } static int ct_whitespace(unsigned char c) { return(state->charset->flags[c]&CTF_WHITESPACE); } static int find_word(int *bp, int *ep, int (*test)(unsigned char), int mustcurs) { int curs; if (iblen <= iplen) return(0); curs = ibcurs; while <"found"> (1) { if (curs <= iplen) { while (1) { if (curs >= iblen) return(0); if ((*test)(ibuf[curs])) break <"found">; curs ++; } } else if ((curs < iblen) && (*test)(ibuf[curs])) { while (1) { if (curs <= iplen) break <"found">; curs --; if (! (*test)(ibuf[curs])) { curs ++; break <"found">; } } } curs --; } if (mustcurs && (curs > ibcurs)) return(0); *bp = curs; while ((curs < iblen) && (*test)(ibuf[curs])) curs ++; if (mustcurs && (curs < ibcurs)) return(0); *ep = curs; return(1); } static void history_save(HISTLIST *list, const unsigned char *txt, int len) { HISTENT *h; int i; if (len < 1) return; h = xget(sizeof(HISTENT)); h->older = list->root->older; h->newer = list->root; h->older->newer = h; h->newer->older = h; h->txt = xget(len); h->len = len; bcopy(txt,h->txt,len); i = *list->sizep - 1; h = h->older; while (h != list->root) { if (i < 1) { HISTENT *h2; h2 = h->older; h->older->newer = h->newer; h->newer->older = h->older; xput(h->txt); xput(h); h = h2; } else { i --; h = h->older; } } } #define DF_SAVE 0x00000001 #define DF_NOMERGE 0x00000002 static void delete_n_at(int at, int n, int flags) { static unsigned int lastsave; if (flags & DF_SAVE) { if ( !(flags & DF_NOMERGE) && ((lastsave == ikeyseq-1) || (lastsave == ikeyseq)) && ((at == ibcurs) || (at+n == ibcurs)) ) { HISTENT *h; h = killring.root->older; if (h->len < 0) panic("nothing on kill ring"); if (at == ibcurs) { h->txt = xreget(h->txt,h->len+n); bcopy(ibuf+at,h->txt+h->len,n); h->len += n; } else { h->txt = xreget(h->txt,h->len+n); bcopy(h->txt,h->txt+n,h->len); bcopy(ibuf+at,h->txt,n); h->len += n; } } else { history_save(&killring,ibuf+at,n); } lastsave = (flags & DF_NOMERGE) ? 0 : ikeyseq; } if (n+at < iblen) bcopy(ibuf+at+n,ibuf+at,iblen-(n+at)); iblen -= n; #define ADJUST(var) do { \ if ((var) >= at) { if ((var) >= at+n) (var) -= n; else (var) = at; } \ } while (0) ADJUST(ibcurs); ADJUST(imark); ADJUST(itempmark); #undef ADJUST } static void insert_n_at(int at, int n, int aftermark) { ibgrow(iblen+n); if (at < iblen) bcopy(ibuf+at,ibuf+at+n,iblen-at); iblen += n; #define ADJUST(var) do { \ if (((var) > at) || (((var) == at) && !aftermark)) { (var) += n; } \ } while (0) ADJUST(ibcurs); ADJUST(imark); ADJUST(itempmark); #undef ADJUST } static unsigned int tcsh_seq; static HISTENT *tcsh_h; static void do_tcsh(HISTENT *(*step)(HISTENT *)) { if (tcsh_seq == ikeyseq-1) { delete_n_at(ibcurs,iblen-ibcurs,0); } else { tcsh_h = histroot.root; } tcsh_seq = ikeyseq; while (1) { tcsh_h = (*step)(tcsh_h); if (tcsh_h == histroot.root) { beep(); return; } if ( (tcsh_h->len >= iblen-iplen) && !bcmp(ibuf+iplen,tcsh_h->txt,iblen-iplen) ) break; } delete_n_at(ibcurs,iblen-ibcurs,0); insert_n_at(ibcurs,tcsh_h->len-(ibcurs-iplen),1); bcopy(tcsh_h->txt,ibuf+iplen,tcsh_h->len); } static void set_temp_mark(int at) { itempmark = at; itempmarkseq = ikeyseq; } static void yank_part1(void) { if (yankseq != ikeyseq-1) yank_h = 0; yankseq = ikeyseq; } static void yank_part2(void) { insert_n_at(ibcurs,yank_h->len,1); bcopy(yank_h->txt,ibuf+ibcurs,yank_h->len); set_temp_mark(ibcurs); ibcurs += yank_h->len; } static void history_np_part1(void) { if (history_np_seq == ikeyseq-1) { delete_n_at(itempmark,ibcurs-itempmark,0); } else { history_np_h = 0; } history_np_seq = ikeyseq; } static void history_np_part2(void) { set_temp_mark(ibcurs); if (history_np_h == histroot.root) { history_np_h = 0; } else { insert_n_at(ibcurs,history_np_h->len,1); bcopy(history_np_h->txt,ibuf+ibcurs,history_np_h->len); ibcurs += history_np_h->len; } } /* KF BEGIN */ static void kf_unbound(void) { beep(); } static void kf_backward_character(void) { if (ibcurs > iplen) ibcurs --; } static void kf_backward_word(void) { while ( (ibcurs > iplen) && ( !ct_wordchar(ibuf[--ibcurs]) || ( (ibcurs > iplen) && ct_wordchar(ibuf[ibcurs-1]) ) ) ); } static void kf_beginning_of_line(void) { ibcurs = iplen; } static void kf_case_word_capitalize(void) { int beg; int end; int first; if (! find_word(&beg,&end,ct_wordchar,0)) return; first = 1; while (beg < end) { switch (state->charset->flags[ibuf[beg]] & CTF_LETTER) { case CTF_LETTER_NOT: first = 1; break; case CTF_LETTER_LC: if (first) ibuf[beg] = state->charset->upcase[ibuf[beg]]; first = 0; break; case CTF_LETTER_UC: if (! first) ibuf[beg] = state->charset->dncase[ibuf[beg]]; first = 0; break; case CTF_LETTER_OTH: first = 0; break; } beg ++; } } static void kf_case_word_lower(void) { int beg; int end; if (! find_word(&beg,&end,ct_wordchar,0)) return; while (beg < end) { switch (state->charset->flags[ibuf[beg]] & CTF_LETTER) { case CTF_LETTER_UC: ibuf[beg] = state->charset->dncase[ibuf[beg]]; break; } beg ++; } } static void kf_case_word_upper(void) { int beg; int end; if (! find_word(&beg,&end,ct_wordchar,0)) return; while (beg < end) { switch (state->charset->flags[ibuf[beg]] & CTF_LETTER) { case CTF_LETTER_LC: ibuf[beg] = state->charset->upcase[ibuf[beg]]; break; } beg ++; } } static void kf_complete(void) { /* XXX */ beep(); } static void kf_delete_next_character(void) { if (ibcurs < iblen) delete_n_at(ibcurs,1,DF_SAVE); } static void kf_delete_previous_character(void) { if (ibcurs > iplen) delete_n_at(ibcurs-1,1,DF_SAVE); } static void kf_end_of_line(void) { ibcurs = iblen; } static void kf_finish_line(void) { ibdone = 1; } static void kf_forward_character(void) { if (ibcurs < iblen) ibcurs ++; } static void kf_forward_word(void) { while ( (ibcurs < iblen) && ( !ct_wordchar(ibuf[ibcurs++]) || ( (ibcurs < iblen) && ct_wordchar(ibuf[ibcurs]) ) ) ); } static void kf_default(void) { if (ct_printable(ilastkey)) kf_self_insert(); else kf_unbound(); } static void kf_delete_next_word(void) { int c0; c0 = ibcurs; kf_forward_word(); if (c0 != ibcurs) delete_n_at(c0,ibcurs-c0,DF_SAVE); } static void kf_delete_previous_word(void) { int c0; c0 = ibcurs; kf_backward_word(); if (c0 != ibcurs) delete_n_at(ibcurs,c0-ibcurs,DF_SAVE); } static void kf_history_n(void) { history_np_part1(); if (! history_np_h) beep(); history_np_h = history_np_h ? history_np_h->newer : histroot.root->newer; history_np_part2(); } static void kf_history_p(void) { history_np_part1(); history_np_h = history_np_h ? history_np_h->older : histroot.root->older; if (history_np_h == histroot.root) beep(); history_np_part2(); } static void kf_interrupt(void) { kill(0,SIGINT); } static void kf_kill_entire_line(void) { delete_n_at(iplen,iblen-iplen,DF_SAVE); } static void kf_kill_region(void) { int m; if (itempmarkseq == ikeyseq-1) { m = itempmark; } else if (imark < 0) { beep(); return; } else { m = imark; } if (m < ibcurs) { delete_n_at(m,ibcurs-m,DF_SAVE); } else { delete_n_at(ibcurs,m-ibcurs,DF_SAVE); } } static void kf_kill_to_end_of_line(void) { delete_n_at(ibcurs,iblen-ibcurs,DF_SAVE); } static void kf_literal_next(void) { static KEYMAP *m = 0; if (m == 0) { int i; m = xget(sizeof(KEYMAP)); lookup_bind("self-insert",&m->key[0]); for (i=1;i<256;i++) m->key[i] = m->key[0]; } curkeymap = m; } static void kf_noop(void) { } static void kf_number_dec(void) { int beg; int end; int i; if (! find_word(&beg,&end,ct_digit,0)) return; i = end - 1; while (1) { if (i < beg) { beep(); return; } if (ibuf[i] != '0') break; i --; } ibuf[i] --; while (1) { i ++; if (i >= end) break; ibuf[i] = '9'; } } static void kf_number_inc(void) { int beg; int end; int i; if (! find_word(&beg,&end,ct_digit,0)) return; i = end - 1; while (1) { if (i < beg) { insert_n_at(beg,1,1); ibuf[beg] = '1'; return; } if (ibuf[i] != '9') break; ibuf[i] = '0'; i --; } ibuf[i] ++; } static void kf_redraw_line(void) { iredraw = 1; putc('\n',shof); } static void kf_ring_yank(void) { if ((yankseq == ikeyseq-1) && (itempmarkseq == ikeyseq-1)) delete_n_at(itempmark,ibcurs-itempmark,0); yank_part1(); if (yank_h == 0) { yank_h = killring.root->older; if (yank_h == killring.root) { yank_h = 0; beep(); return; } } else { yank_h = yank_h->older; if (yank_h == killring.root) { yank_h = 0; beep(); return; } } yank_part2(); } static void kf_self_insert(void) { insert_n_at(ibcurs,1,1); ibuf[ibcurs++] = ilastkey; } static void kf_set_mark(void) { imark = ibcurs; } static void kf_show_completions(void) { /* XXX */ beep(); } static void kf_tcsh_history_n(void) { static HISTENT *x(HISTENT *h) { return(h->newer); } do_tcsh(x); } static void kf_tcsh_history_p(void) { static HISTENT *x(HISTENT *h) { return(h->older); } do_tcsh(x); } static void kf_transpose_after(void) { if (ibcurs+2 <= iblen) { unsigned char t; t = ibuf[ibcurs]; ibuf[ibcurs] = ibuf[ibcurs+1]; ibuf[ibcurs+1] = t; } } static void kf_transpose_around(void) { if ((ibcurs > iplen) && (ibcurs < iblen)) { unsigned char t; t = ibuf[ibcurs-1]; ibuf[ibcurs-1] = ibuf[ibcurs]; ibuf[ibcurs] = t; } } static void kf_transpose_before(void) { if (ibcurs-iplen >= 2) { unsigned char t; t = ibuf[ibcurs-1]; ibuf[ibcurs-1] = ibuf[ibcurs-2]; ibuf[ibcurs-2] = t; } } static void kf_yank(void) { yank_part1(); if (yank_h == 0) { yank_h = killring.root->older; if (yank_h == killring.root) { yank_h = 0; beep(); return; } } yank_part2(); } /* KF END */ static void type_leaf(KEYFUNC *kf) { switch (kf->type) { case KFT_BUILTIN: (*kf->u.builtin)(); ibdirty = 1; break; default: panic("unknown keyfunc type %d (kf %p, key %02x)",kf->type,(void *)kf,ilastkey); break; } } static int get_line_tty(const char *prompt, const struct termios *o_tio) { int rv; unsigned char c; static struct termios tio; tio = *o_tio; tio.c_iflag &= ~(PARMRK|IMAXBEL|INLCR|IGNCR|ICRNL|IXOFF); tio.c_lflag &= ~(ECHOKE|ECHOE|ECHO|ECHONL|ECHOPRT|ECHOCTL|ISIG|ICANON|IEXTEN|EXTPROC|FLUSHO); tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; tcsetattr(0,TCSASOFT,&tio); curkeymap = &rootmap; iplen = strlen(prompt); iblen = iplen; ibcurs = iplen; ibgrow(iblen); bcopy(prompt,ibuf,iplen); iredraw = 1; ibdirty = 1; ibdone = 0; imark = -1; itempmarkseq = ikeyseq - 2; while (! ibdone) { struct pollfd pfd[2]; pfd[0].fd = 0; pfd[0].events = POLLIN | POLLRDNORM; pfd[1].fd = sigchld_rpipe; pfd[1].events = POLLIN | POLLRDNORM; if (! ibdirty) fflush(shof); rv = poll(&pfd[0],2,ibdirty?0:INFTIM); if (rv < 0) { if (errno == EINTR) continue; fprintf(stderr,"poll: %s\n",strerror(errno)); exit(1); } if (rv == 0) { update_line(); continue; } if (pfd[1].revents) { reset_sigchld_pipe(); /* XXX handle death; if it produces output, then do iredraw = 1; and ibdirty = 1; */ continue; } rv = read(0,&c,1); switch (rv) { case -1: if (errno == EINTR) continue; fprintf(stderr,"read: %s\n",strerror(errno)); exit(1); break; case 0: fprintf(stderr,"read EOF?""?\n"); exit(1); break; case 1: break; default: fprintf(stderr,"read returned %d?""?\n",rv); exit(1); break; } if ( (iblen == iplen) && (c == (unsigned char)o_tio->c_cc[VEOF]) && (curkeymap == &rootmap) ) { return(0); } ilastkey = c; ikeyseq ++; switch (curkeymap->key[c].type) { case KT_UNBOUND: kf_unbound(); curkeymap = &rootmap; break; case KT_LEAF: { KEYFUNC *kf; kf = curkeymap->key[c].u.leaf; curkeymap = &rootmap; type_leaf(kf); } break; case KT_MAP: ikeyseq --; curkeymap = curkeymap->key[c].u.map; break; default: fprintf(stderr,"bad type %d (map %p key %02x)\n",curkeymap->key[c].type,(void *)curkeymap,c); curkeymap = &rootmap; break; } } ibuf[iblen] = '\0'; ilen = iblen - iplen; iline = xstrdup(ibuf+iplen,ilen); putc('\n',shof); return(1); } static int get_line_fd(void) { char *b; int l; int a; char c; int rv; b = xget((a=16)+1); l = 0; while (1) { rv = read(0,&c,1); switch (rv) { case -1: fprintf(stderr,"%s: input read error: %s\n",__progname,strerror(errno)); exit(1); break; case 0: if (l == 0) { xput(b); return(0); } b[l] = '\0'; iline = b; ilen = l; return(1); break; case 1: if (c == '\n') { b[l] = '\0'; iline = b; ilen = l; return(1); } if (l >= a) b = xreget(b,(a=l+16)+1); b[l++] = c; break; default: fprintf(stderr,"%s: input read returned %d (expecting -1, 0, 1)\n",__progname,rv); exit(1); break; } } } static int get_line(const char *prompt) { struct termios tio; int rv; if (tcgetattr(0,&tio) == 0) { rv = get_line_tty(prompt,&tio); tcsetattr(0,TCSASOFT,&tio); } else { rv = get_line_fd(); } return(rv); } static void build_word_token(int (*get)(int), int (*term)(unsigned char, int), TOKEN *t, int *lenp, CHF *flagp) { CHF *buff; CHF f; unsigned int wf; char *buf; int have; int len; int j; int c; buff = 0; buf = xget(1); have = 0; len = 0; f = 0; wf = 0; for <"charloop"> (j=0;;j++) { c = (*get)(j); if (c < 0) break; if (f & CHF_BQUOTE) { switch (c) { case 'a': c = '\a'; break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case '_': c = ' '; break; } } else if ((c == '\\') && !(f & CHF_SQUOTE)) { f |= CHF_BQUOTE; wf |= TOK_WF_QUOTE; continue; } else if ((f & CHF_SQUOTE) && (c == '\'')) { f &= ~CHF_SQUOTE; continue; } else if ((f & CHF_DQUOTE) && (c == '"')) { f &= ~CHF_DQUOTE; continue; } else if (! f) { switch (c) { case '\'': f |= CHF_SQUOTE; wf |= TOK_WF_QUOTE; continue; break; case '"': f |= CHF_DQUOTE; wf |= TOK_WF_QUOTE; continue; break; } if ((*term)(c,j)) break <"charloop">; } if (f && !buff) { int k; buff = xget((have+1)*sizeof(CHF)); for (k=0;k= have) { buf = xreget(buf,(have=(len+16))+1); if (buff) buff = xreget(buff,(have+1)*sizeof(CHF)); } buf[len] = c; if (buff) buff[len] = f | c; f &= ~CHF_BQUOTE; len ++; } buf[len] = '\0'; if (buff) buff[len] = CHF_TERM; t->type = TOK_WORDTEXT; t->u.w.flags = wf; t->u.w.l = len; t->u.w.s = buf; t->u.w.sf = buff; *lenp = j; *flagp = f; } static TOKEN *tok_new(void) { return(xget(sizeof(TOKEN))); } static void tok_free_sub(TOKEN *t) { switch (t->type) { case TOK_WORDTEXT: xput(t->u.w.s); xput(t->u.w.sf); break; default: break; } } static void tok_free(TOKEN *t) { tok_free_sub(t); xput(t); } static void free_line_tokens(void) { int i; for (i=0;i>&", TOK_GGTAND }, { ">||", TOK_GTPPIPE }, { "(``", TOK_LBQQ }, { "<|", TOK_LTPIPE }, { ">&", TOK_GTAND }, { ">>", TOK_GGT }, { ">|", TOK_GTPIPE }, { "|)", TOK_PRIGHT }, { "|-", TOK_PMINUS }, { "|:", TOK_PCOLON }, { "|;", TOK_PSEMI }, { "||", TOK_OROR }, { "&&", TOK_ANDAND }, { "&)", TOK_ANDR }, { "(&", TOK_ANDL }, { "(|", TOK_PLEFT }, { "(!", TOK_LBANG }, { "(`", TOK_LBQ }, { "`)", TOK_RBQ }, { "(?", TOK_CIF }, { "?-", TOK_CTHEN }, { "?;", TOK_CELSE }, { "&", TOK_AND }, { "(", TOK_LPAR }, { ")", TOK_RPAR }, { ";", TOK_SEMI }, { "<", TOK_LT }, { ">", TOK_GT }, { "|", TOK_PIPE }, { 0 } }; /* SPECIAL_BEGINNERS is the list of characters that can begin specials. SPECIAL_SOLO is the subset of this list that are one-character specials, meaning that if such a character is seen, a special _will_ be found; SPECIAL_NONSOLO is SPECIAL_BEGINNERS minus SPECIAL_SOLO. _SOLO and _NONSOLO should be undefined if the corresponding class is empty. All three (when defined) should suitable for "case XXX:" use. */ #define SPECIAL_BEGINNERS \ '&': case '(': case ')': case ';': case '?': \ case '<': case '>': case '`': case '|' #define SPECIAL_SOLO \ '&': case '(': case ')': case ';': \ case '<': case '>': case '|' #define SPECIAL_NONSOLO \ '`': case '?' int i; int ll; TOKEN t; char *ilp; static void consume(int n) { ilp += n; ll -= n; } static void gottok(int type, int len) { TOKEN *new; t.type = type; consume(len); new = tok_new(); *new = t; if (ntokens >= atokens) tokens = xreget(tokens,(atokens=ntokens+16)*sizeof(TOKEN *)); tokens[ntokens++] = new; if (type != TOK_EOL) goto gottoken; } if (specials[0].len == 0) { for (i=0;specials[i].str;i++) specials[i].len = strlen(specials[i].str); } ilp = iline; ll = ilen; while (1) { if (ll < 1) break; switch (ilp[0]) { case ' ': case '\t': case '\n': i = 1; while <"white"> (1) { switch (ilp[i]) { case ' ': case '\t': case '\n': break; default: break <"white">; } i ++; } gottok(TOK_WHITESPACE,i); break; case SPECIAL_BEGINNERS: for (i=0;specials[i].str;i++) { if (ll < specials[i].len) continue; if (!bcmp(ilp,specials[i].str,specials[i].len)) { gottok(specials[i].tokval,specials[i].len); } } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { int gotand; int v; int j; gotand = 0; v = 0; j = 0; while (j < ll) { switch (ilp[j]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': v = (10 * v) + (ilp[j++] - '0'); if (gotand) gotand = 2; continue; break; case '&': if (! gotand) { t.u.n.n = v; v = 0; gotand = 1; j ++; continue; } break; } break; } switch (gotand) { case 1: if ((ll-j >= 1) && (ilp[j] == '-')) { gottok(TOK_N_AND_NIL,j+1); } break; default: t.u.n.m = v; gottok(TOK_N_AND_M,j); break; case 0: t.u.n.n = v; if (ll-j >= 3) { if (ilp[j+1] == ilp[j+2]) { if (ilp[j] == '<') { switch (ilp[j+1]) { case '>': gottok(TOK_N_LTGGT,j+3); break; case '|': gottok(TOK_N_LTPPIPE,j+3); break; } } else if ((ilp[j] == '>') && (ilp[j+1] == '|')) { gottok(TOK_N_GTPPIPE,j+3); } } } if (ll-j >= 2) { switch (ilp[j]) { case '<': switch (ilp[j+1]) { case '>': gottok(TOK_N_LTGT,j+2); break; case '|': gottok(TOK_N_LTPIPE,j+2); break; } break; case '>': switch (ilp[j+1]) { case '>': gottok(TOK_N_GGT,j+2); break; case '|': gottok(TOK_N_GTPIPE,j+2); break; } break; } } if (ll-j >= 1) { switch (ilp[j]) { case '<': gottok(TOK_N_LT,j+1); break; case '>': gottok(TOK_N_GT,j+1); break; } } break; } } break; } { CHF eflags; static int get(int x) { return((x"); break; case TOK_GGT: return(">>"); break; case TOK_GTAND: return(">&"); break; case TOK_GGTAND: return(">>&"); break; case TOK_N_LT: return("N<"); break; case TOK_N_GT: return("N>"); break; case TOK_N_GGT: return("N>>"); break; case TOK_N_LTGT: return("N<>"); break; case TOK_N_LTGGT: return("N<>>"); break; case TOK_N_AND_M: return("N&M"); break; case TOK_N_AND_NIL: return("N&-"); break; case TOK_LTPIPE: return("<|"); break; case TOK_GTPIPE: return(">|"); break; case TOK_N_LTPIPE: return("N<|"); break; case TOK_N_GTPIPE: return("N>|"); break; case TOK_LTPPIPE: return("<||"); break; case TOK_GTPPIPE: return(">||"); break; case TOK_N_LTPPIPE: return("N<||"); break; case TOK_N_GTPPIPE: return("N>||"); break; case TOK_LBQQ: return("(``"); break; case TOK_LBQ: return("(`"); break; case TOK_RBQ: return("`)"); break; case TOK_CIF: return("(?"); break; case TOK_CTHEN: return("?-"); break; case TOK_CELSE: return("?;"); break; } panic("invalid toktype"); } static CHF token_chf(TOKEN *t, int o) { if (t->type != TOK_WORDTEXT) panic("bad token_chf"); if ((o < 0) || (o > t->u.w.l)) panic("bad offset"); if (o == t->u.w.l) return(CHF_TERM); return(t->u.w.sf?t->u.w.sf[o]:t->u.w.s[o]); } static void builtin__percent(void) { shp("%%: not implemented\n"); } static int cmdline_builtin(void) { if ( (ntokens == 1) && (tokens[0]->type == TOK_WORDTEXT) && (token_chf(tokens[0],0) == '%') ) { builtin__percent(); return(1); } return(0); } static PT *pt_new(void) { PT *pt; pt = xget(sizeof(PT)); pt->type = PT_NONE; pt->flags = 0; return(pt); } static void command_init(PT *new) { new->type = PT_COMMAND; new->u.command.type = PT_CMD_NONE; new->u.command.n_redir = 0; new->u.command.redirs = 0; new->u.command.env = 0; } static PT *command_new(void) { PT *new; new = pt_new(); command_init(new); return(new); } static PT *redir_new(void) { PT *new; new = pt_new(); new->type = PT_REDIR; new->u.redir.type = PT_REDIR_NONE; new->u.redir.refcnt = 0; new->u.redir.usecnt = 0; return(new); } static TOKEN *tok_next(unsigned int flags) #define TNF_GETWS 0x00000001 { while ((tokens[attoken]->type == TOK_WHITESPACE) && !(flags & TNF_GETWS)) attoken ++; return(tokens[attoken]); } static void tok_skip(void) { if (tokens[attoken]->type == TOK_EOL) panic("skipping EOL"); attoken ++; } static TOKEN *tok_next_n(unsigned int flags, int n) { int saveat; int at; saveat = attoken; for (;n>0;n--) { tok_next(flags); tok_skip(); } tok_next(flags); at = attoken; attoken = saveat; return(tokens[at]); } static PTRLIST *ptrlist_push(PTRLIST *l, void *p) { PTRLIST *t; t = xget(sizeof(PTRLIST)); t->p = p; t->link = l; return(t); } static void *ptrlist_pop(PTRLIST **l) { void *rv; PTRLIST *t; t = *l; rv = t->p; *l = t->link; xput(t); return(rv); } static void cwd_incref(CWD *c) { c->refcnt ++; } static void cwd_decref(CWD *c) { c->refcnt --; if (c->refcnt > 0) return; if (c->refcnt < 0) panic("cwd refcnt negative"); shfd_close(c->fd); xput(c); } static void env_incref(ENVIRONMENT *e) { e->refcnt ++; } static void env_decref(ENVIRONMENT *e) { int i; e->refcnt --; if (e->refcnt > 0) return; if (e->refcnt < 0) panic("env refcnt negative"); for (i=0;invars;i++) xput(e->vars[i]); xput(e->vars); xput(e); } static void path_incref(PATH *p) { p->refcnt ++; } static void path_decref(PATH *p) { p->refcnt --; if (p->refcnt > 0) return; if (p->refcnt < 0) panic("path refcnt negative"); xput(p->strbuf); xput(p->strs); xput(p); } static SHELLSTATE *shellstate_copy(SHELLSTATE *o) { SHELLSTATE *n; n = xget(sizeof(SHELLSTATE)); *n = *o; cwd_incref(n->cwd); env_incref(n->env); path_incref(n->path); return(n); } static void shellstate_free(SHELLSTATE *ss) { cwd_decref(ss->cwd); env_decref(ss->env); path_decref(ss->path); xput(ss); } static void pt_free(PT *); /* forward */ static void free_wordpart_sub(WORDPART *wp) { switch (wp->type) { case WPT_TEXT: break; case WPT_BQ: pt_free(wp->u.bq); break; default: panic("bad wordpart type %d",wp->type); break; } } static void free_wordpart(WORDPART *wp) { free_wordpart_sub(wp); xput(wp); } static void fdenv_free(FDENV *e) { xput(e->list); xput(e); } static void fdenv_deref(FDENV *e) { int i; FDFD *f; PT *r; PT *pp; int n; e->refs --; if (e->refs > 0) return; if (e->refs < 0) panic("negative refs"); for (i=e->n-1;i>=0;i--) { f = &e->list[i]; switch (f->type) { default: panic("bad fdfd type"); break; case FDFD_SHELL: break; case FDFD_FILE: r = f->u.redir; if ( (r->type != PT_REDIR) || (r->u.redir.type != PT_REDIR_FILE) ) panic("file redir isn't"); r->u.redir.u.file.refs --; if (r->u.redir.u.file.refs < 0) panic("negative refs"); if (r->u.redir.u.file.refs == 0) { close(r->u.redir.u.file.shfd); r->u.redir.u.file.shfd = -1; } break; case FDFD_PIPE: r = f->u.redir; if ( (r->type != PT_REDIR) || (r->u.redir.type != PT_REDIR_PIPE) ) panic("pipe redir isn't"); pp = r->u.redir.u.pipe.parallel; if ( (pp->type != PT_COMMAND) || (pp->u.command.type != PT_CMD_PARALLEL) ) panic("pipe's parallel isn't"); if (PIPENO_SPECIAL(r->u.redir.u.pipe.pipeno)) panic("special pipe"); if ( (r->u.redir.u.pipe.pipeno < 0) || (r->u.redir.u.pipe.pipeno >= pp->u.command.u.parallel.n_pipes) ) { panic("impossible pipe number"); } n = --pp->u.command.u.parallel.pipes[r->u.redir.u.pipe.pipeno].refs[r->u.redir.u.pipe.which]; if (n < 0) panic("negative refs"); if (n == 0) { shfd_close(pp->u.command.u.parallel.pipes[r->u.redir.u.pipe.pipeno].fds[r->u.redir.u.pipe.which]); pp->u.command.u.parallel.pipes[r->u.redir.u.pipe.pipeno].fds[r->u.redir.u.pipe.which] = -1; } break; case FDFD_BQOUT: pp = f->u.bq; if (pp->u.bq.rtflags & PT_BQF_HAVE_W) { shfd_close(pp->u.bq.pipe[1]); pp->u.bq.rtflags &= ~PT_BQF_HAVE_W; } break; } } fdenv_free(e); } static void bq_list(PT *p) { if (p->type != PT_BQ) panic("non-bq"); if (p->u.bq.rtflags & PT_BQF_LISTED) panic("listed bq"); p->u.bq.rtflags |= PT_BQF_LISTED; p->u.bq.flink = bqhead; p->u.bq.blink = 0; if (bqhead) { if (bqhead->u.bq.flink->type != PT_BQ) panic("non-bq"); bqhead->u.bq.blink = p; } else { bqtail = p; } bqhead = p; } static void bq_delist(PT *p) { if (p->type != PT_BQ) panic("non-bq"); if (! (p->u.bq.rtflags & PT_BQF_LISTED)) panic("unlisted bq"); p->u.bq.rtflags &= ~PT_BQF_LISTED; if (p->u.bq.flink) { if (p->u.bq.flink->type != PT_BQ) panic("non-bq"); p->u.bq.flink->u.bq.blink = p->u.bq.blink; } else { bqtail = p->u.bq.blink; } if (p->u.bq.blink) { if (p->u.bq.blink->type != PT_BQ) panic("non-bq"); p->u.bq.blink->u.bq.flink = p->u.bq.flink; } else { bqhead = p->u.bq.flink; } } static void pt_makenone(PT *pt) { int i; switch (pt->type) { default: panic("invalid pt type %d to pt_makenone",pt->type); break; case PT_NONE: break; case PT_CMDLINE: pt_free(pt->u.cmdline.seq); for (i=pt->u.cmdline.ntok-1;i>=0;i--) tok_free(pt->u.cmdline.toks[i]); xput(pt->u.cmdline.toks); break; case PT_COMMAND: switch (pt->u.command.type) { default: panic("invalid command type"); break; case PT_CMD_NONE: case PT_CMD_PAREN: case PT_CMD_NOWAIT: case PT_CMD_BG: pt_free(pt->u.command.u.seq); break; case PT_CMD_SUBSHELL: pt_free(pt->u.command.u.subshell.seq); shellstate_free(pt->u.command.u.subshell.state); break; case PT_CMD_SIMPLE: if (pt->u.command.u.simple.p) { shp("Warning: CMD_SIMPLE PT %p has proc %p\n",(void *)pt,(void *)pt->u.command.u.simple.p); if (pt->u.command.u.simple.p->pt != pt) panic("kid backpointer wrong"); pt->u.command.u.simple.p->pt = 0; } pt_free(pt->u.command.u.simple.wordlist); if (pt->u.command.env) fdenv_deref(pt->u.command.env); break; case PT_CMD_PARALLEL: pt_free(pt->u.command.u.parallel.name); for (i=pt->u.command.u.parallel.n_pipes-1;i>=0;i--) { pt_free(pt->u.command.u.parallel.pipes[i].name); shfd_close(pt->u.command.u.parallel.pipes[i].fds[0]); shfd_close(pt->u.command.u.parallel.pipes[i].fds[1]); } xput(pt->u.command.u.parallel.pipes); for (i=pt->u.command.u.parallel.n_seq-1;i>=0;i--) { pt_free(pt->u.command.u.parallel.seqs[i]); } xput(pt->u.command.u.parallel.seqs); break; case PT_CMD_COND: pt_free(pt->u.command.u.cond.test); pt_free(pt->u.command.u.cond.ift); pt_free(pt->u.command.u.cond.iff); break; } for (i=pt->u.command.n_redir-1;i>=0;i--) { pt_free(pt->u.command.redirs[i]); } xput(pt->u.command.redirs); break; case PT_SEQUENCE: case PT_OROR: case PT_ANDAND: case PT_PIPELINE: for (i=pt->u.pts.n-1;i>=0;i--) pt_free(pt->u.pts.list[i]); xput(pt->u.pts.list); break; case PT_WORDLIST: for (i=pt->u.wl.n-1;i>=0;i--) pt_free(pt->u.wl.list[i]); xput(pt->u.wl.list); break; case PT_REDIR: switch (pt->u.redir.type) { default: panic("invalid redir type"); break; case PT_REDIR_NONE: break; case PT_REDIR_FILE: pt_free(pt->u.redir.u.file.word); if (pt->u.redir.u.file.shfd >= 0) close(pt->u.redir.u.file.shfd); break; case PT_REDIR_FILEAND: pt_free(pt->u.redir.u.fileand.word); break; case PT_REDIR_DUP: break; case PT_REDIR_PIPE: pt_free(pt->u.redir.u.pipe.word); break; } if (pt->u.redir.refcnt) panic("freeing refed redir (refcnt %d)",pt->u.redir.refcnt); break; case PT_WORD: if (pt->u.word.flags & PT_WF_TEXT) { xput(pt->u.word.u.text); } else { for (i=pt->u.word.len-1;i>=0;i--) free_wordpart_sub(&pt->u.word.u.parts[i]); xput(pt->u.word.u.parts); } break; case PT_BQ: pt_free(pt->u.bq.seq); if (pt->u.bq.rtflags & PT_BQF_LISTED) bq_delist(pt); break; } pt->type = PT_NONE; } static void pt_justfree(PT *pt) { xput(pt); } static void pt_free(PT *pt) { if (pt == 0) return; pt_makenone(pt); pt_justfree(pt); } static unsigned int bq_flags(TOKEN *t) { unsigned int f; int i; CHF c; f = 0; for (i=0;;i++) { c = token_chf(t,i); if (c == CHF_TERM) return(f); switch (c & CHF_CHAR) { case 'q': f |= PT_BQF_Q_ALL; break; case 'n': f |= PT_BQF_NEWLINE; break; case 'l': f |= PT_BQF_Z_LEAD; break; case 't': f |= PT_BQF_Z_TRAIL; break; case 'z': f |= PT_BQF_Z_OTHER; break; case 'e': f |= PT_BQF_ERROR; break; case 'Q': f &= ~PT_BQF_Q_ALL; break; case 'N': f &= ~PT_BQF_NEWLINE; break; case 'L': f &= ~PT_BQF_Z_LEAD; break; case 'T': f &= ~PT_BQF_Z_TRAIL; break; case 'Z': f &= ~PT_BQF_Z_OTHER; break; case 'E': f &= ~PT_BQF_ERROR; break; default: shp("Invalid %s flag character %c ignored\n",tokname(TOK_LBQQ),c&CHF_CHAR); break; } } } static int print_token_text(FILE *to, TOKEN *t) { switch (t->type) { case TOK_EOL: fprintf(to,""); return(5); break; case TOK_WHITESPACE: fprintf(to," "); return(1); break; case TOK_WORDTEXT: if (t->u.w.l < 1) { fprintf(to,"\"\""); return(2); } else if (! t->u.w.sf) { fwrite(t->u.w.s,1,t->u.w.l,to); if (t->u.w.flags & TOK_WF_QUOTE) { fprintf(to,"\"\""); return(t->u.w.l+2); } return(t->u.w.l); } else { int i; CHF f; int n; n = 0; f = 0; for (i=0;iu.w.l;i++) { CHF c; c = t->u.w.sf[i]; if ((c ^ f) & CHF_SQUOTE) { putc('\'',to); n ++; f ^= CHF_SQUOTE; } if ((c ^ f) & CHF_DQUOTE) { putc('"',to); n ++; f ^= CHF_DQUOTE; } if (c & CHF_BQUOTE) { putc('\\',to); n ++; } putc(c&CHF_CHAR,to); n ++; } if (f & CHF_SQUOTE) { putc('\'',to); n ++; } if (f & CHF_DQUOTE) { putc('"',to); n ++; } return(n); } break; case TOK_N_LT: { const char *suf; suf = "<"; if (0) { case TOK_N_GT: suf = ">"; } if (0) { case TOK_N_GGT: suf = ">>"; } if (0) { case TOK_N_LTGT: suf = "<>"; } if (0) { case TOK_N_LTGGT: suf = "<>>"; } if (0) { case TOK_N_AND_NIL: suf = "&-"; } if (0) { case TOK_N_LTPIPE: suf = "<|"; } if (0) { case TOK_N_GTPIPE: suf = ">|"; } if (0) { case TOK_N_LTPPIPE: suf = "<||"; } if (0) { case TOK_N_GTPPIPE: suf = ">||"; } return(fprintf(to,"%d%s",t->u.n.n,suf)); } break; case TOK_N_AND_M: return(fprintf(to,"%d&%d",t->u.n.n,t->u.n.m)); break; case TOK_AND: case TOK_LPAR: case TOK_RPAR: case TOK_LBANG: case TOK_PLEFT: case TOK_PCOLON: case TOK_PMINUS: case TOK_PSEMI: case TOK_PRIGHT: case TOK_ANDL: case TOK_ANDANDL: case TOK_ANDR: case TOK_SEMI: case TOK_OROR: case TOK_ANDAND: case TOK_PIPE: case TOK_LT: case TOK_GT: case TOK_GGT: case TOK_GTAND: case TOK_GGTAND: case TOK_LTPIPE: case TOK_GTPIPE: case TOK_LTPPIPE: case TOK_GTPPIPE: case TOK_LBQQ: case TOK_LBQ: case TOK_RBQ: case TOK_CIF: case TOK_CTHEN: case TOK_CELSE: return(fprintf(to,"%s",tokname(t->type))); break; } panic("bad token type %d to print_token_text",t->type); } static void print_tokpt(void) { int i; int f; int l; int w; f = attoken - 4; l = attoken + 4; if (f < 0) f = 0; if (l > ntokens) l = ntokens; w = 0; if (f > 0) { shp("..."); w += 4; } for (i=f;itype != PT_COMMAND) || (r->type != PT_REDIR)) panic("adding non-redir"); cmd->u.command.redirs = xreget(cmd->u.command.redirs,(cmd->u.command.n_redir+1)*sizeof(PT *)); bcopy(cmd->u.command.redirs,cmd->u.command.redirs+1,cmd->u.command.n_redir*sizeof(PT *)); cmd->u.command.redirs[0] = r; cmd->u.command.n_redir ++; } static void add_redirection_outside(PT *cmd, PT *r) { if ((cmd->type != PT_COMMAND) || (r->type != PT_REDIR)) panic("adding non-redir"); cmd->u.command.redirs = xreget(cmd->u.command.redirs,(cmd->u.command.n_redir+1)*sizeof(PT *)); cmd->u.command.redirs[cmd->u.command.n_redir++] = r; } static void add_redirection(PT *cmd, PT *r) { switch (redir_order) { default: panic("invalid redir_order"); break; case REDIR_ORDER_LTOR: add_redirection_inside(cmd,r); break; case REDIR_ORDER_RTOL: add_redirection_outside(cmd,r); break; } } static PT *parse_sequence(void); /* forward */ static PT *parse_bq(void) { int bqt; TOKEN *t; PT *sub; unsigned int f; PT *new; bqt = tok_next(0)->type; tok_skip(); switch (bqt) { case TOK_LBQQ: t = tok_next(TNF_GETWS); if (t->type != TOK_WORDTEXT) { shp("Missing flags after %s",tokname(TOK_LBQQ)); print_tokpt(); return(0); } f = bq_flags(t); tok_skip(); break; case TOK_LBQ: f = 0; break; default: panic("invalid parse_bq"); break; } sub = parse_sequence(); if (sub == 0) { shp("Missing command inside %s %s\n",tokname(bqt),tokname(TOK_RBQ)); print_tokpt(); return(0); } t = tok_next(0); if (t->type != TOK_RBQ) { shp("Improperly closed %s\n",tokname(bqt)); print_tokpt(); pt_free(sub); return(0); } tok_skip(); new = pt_new(); new->type = PT_BQ; new->u.bq.bqflags = f; new->u.bq.rtflags = 0; new->u.bq.px = -1; new->u.bq.seq = sub; return(new); } static PT *parse_word(unsigned int flags) { TOKEN *t; PT *new; int prevtype; static WORDPART **pv = 0; static WORDPART **pp = 0; static int apv = 0; static int pvo = 0; int nparts; int i; static WORDPART *nextpart(void) { WORDPART *wp; if (nparts+pvo >= apv) { pv = xreget(pv,(apv=nparts+pvo+16)*sizeof(WORDPART *)); pp = pv + pvo; } wp = xget(sizeof(WORDPART)); pp[nparts++] = wp; return(wp); } new = pt_new(); new->type = PT_WORD; new->u.word.flags = flags; new->u.word.len = 0; new->u.word.u.parts = 0; nparts = 0; tok_next(0); while <"moreparts"> (1) { t = tok_next(TNF_GETWS); switch (t->type) { case TOK_WORDTEXT: if (prevtype == TOK_WORDTEXT) break <"moreparts">; *nextpart() = (WORDPART){.type=WPT_TEXT, .u={.text=t}}; tok_skip(); break; case TOK_LBQQ: case TOK_LBQ: { PT *bq; pvo += nparts; pp = pv + pvo; bq = parse_bq(); pvo -= nparts; pp = pv + pvo; if (! bq) { for (i=0;iu.word.flags |= PT_WF_ANYBQ; } break; default: break <"moreparts">; } } if (nparts < 1) { pt_free(new); return(0); } new->u.word.len = nparts; new->u.word.u.parts = xget(nparts*sizeof(WORDPART)); for (i=0;iu.word.u.parts[i] = *pp[i]; xput(pp[i]); } return(new); } static PT *parse_redirection(void) { PT *new; int redirtok; PT **wordfollows; int pipeword; new = redir_new(); pipeword = 0; redirtok = tok_next(0)->type; switch (redirtok) { default: new->type = PT_NONE; pt_free(new); return(0); break; case TOK_LT: new->u.redir.type = PT_REDIR_FILE; new->u.redir.n = 0; new->u.redir.u.file.oflags = O_RDONLY; wordfollows = &new->u.redir.u.file.word; break; case TOK_GT: new->u.redir.type = PT_REDIR_FILE; new->u.redir.n = 1; new->u.redir.u.file.oflags = O_WRONLY|O_CREAT|O_TRUNC; wordfollows = &new->u.redir.u.file.word; break; case TOK_GGT: new->u.redir.type = PT_REDIR_FILE; new->u.redir.n = 1; new->u.redir.u.file.oflags = O_WRONLY|O_CREAT|O_APPEND; wordfollows = &new->u.redir.u.file.word; break; case TOK_GTAND: new->u.redir.type = PT_REDIR_FILEAND; new->u.redir.u.fileand.oflags = O_WRONLY|O_CREAT|O_TRUNC; wordfollows = &new->u.redir.u.fileand.word; break; case TOK_GGTAND: new->u.redir.type = PT_REDIR_FILEAND; new->u.redir.u.fileand.oflags = O_WRONLY|O_CREAT|O_APPEND; wordfollows = &new->u.redir.u.fileand.word; break; case TOK_N_LT: new->u.redir.type = PT_REDIR_FILE; new->u.redir.n = tok_next(0)->u.n.n; new->u.redir.u.file.oflags = O_RDONLY; wordfollows = &new->u.redir.u.file.word; break; case TOK_N_GT: new->u.redir.type = PT_REDIR_FILE; new->u.redir.n = tok_next(0)->u.n.n; new->u.redir.u.file.oflags = O_WRONLY|O_CREAT|O_TRUNC; wordfollows = &new->u.redir.u.file.word; break; case TOK_N_GGT: new->u.redir.type = PT_REDIR_FILE; new->u.redir.n = tok_next(0)->u.n.n; new->u.redir.u.file.oflags = O_WRONLY|O_CREAT|O_APPEND; wordfollows = &new->u.redir.u.file.word; break; case TOK_N_LTGT: new->u.redir.type = PT_REDIR_FILE; new->u.redir.n = tok_next(0)->u.n.n; new->u.redir.u.file.oflags = O_RDWR|O_CREAT; wordfollows = &new->u.redir.u.file.word; break; case TOK_N_LTGGT: new->u.redir.type = PT_REDIR_FILE; new->u.redir.n = tok_next(0)->u.n.n; new->u.redir.u.file.oflags = O_RDWR|O_CREAT|O_APPEND; wordfollows = &new->u.redir.u.file.word; break; case TOK_N_AND_M: new->u.redir.type = PT_REDIR_DUP; new->u.redir.n = tok_next(0)->u.n.n; new->u.redir.u.dup.m = tok_next(0)->u.n.m; wordfollows = 0; break; case TOK_N_AND_NIL: new->u.redir.type = PT_REDIR_DUP; new->u.redir.n = tok_next(0)->u.n.n; new->u.redir.u.dup.m = DUP_CLOSE; wordfollows = 0; break; case TOK_LTPIPE: new->u.redir.type = PT_REDIR_PIPE; new->u.redir.n = 0; new->u.redir.u.pipe.which = 0; wordfollows = &new->u.redir.u.pipe.word; pipeword = 1; break; case TOK_GTPIPE: new->u.redir.type = PT_REDIR_PIPE; new->u.redir.n = 1; new->u.redir.u.pipe.which = 1; wordfollows = &new->u.redir.u.pipe.word; pipeword = 1; break; case TOK_N_LTPIPE: new->u.redir.type = PT_REDIR_PIPE; new->u.redir.n = tok_next(0)->u.n.n; new->u.redir.u.pipe.which = 0; wordfollows = &new->u.redir.u.pipe.word; pipeword = 1; break; case TOK_N_GTPIPE: new->u.redir.type = PT_REDIR_PIPE; new->u.redir.n = tok_next(0)->u.n.n; new->u.redir.u.pipe.which = 1; wordfollows = &new->u.redir.u.pipe.word; pipeword = 1; break; case TOK_LTPPIPE: new->u.redir.type = PT_REDIR_PIPE; new->u.redir.n = 0; new->u.redir.u.pipe.which = 0; new->u.redir.u.pipe.word = 0; wordfollows = 0; break; case TOK_GTPPIPE: new->u.redir.type = PT_REDIR_PIPE; new->u.redir.n = 1; new->u.redir.u.pipe.which = 1; new->u.redir.u.pipe.word = 0; wordfollows = 0; break; case TOK_N_LTPPIPE: new->u.redir.type = PT_REDIR_PIPE; new->u.redir.n = tok_next(0)->u.n.n; new->u.redir.u.pipe.which = 0; new->u.redir.u.pipe.word = 0; wordfollows = 0; break; case TOK_N_GTPPIPE: new->u.redir.type = PT_REDIR_PIPE; new->u.redir.n = tok_next(0)->u.n.n; new->u.redir.u.pipe.which = 1; new->u.redir.u.pipe.word = 0; wordfollows = 0; break; } switch (new->u.redir.type) { case PT_REDIR_FILE: new->u.redir.u.file.shfd = -1; new->u.redir.u.file.refs = 0; break; case PT_REDIR_PIPE: new->u.redir.u.pipe.parallel = 0; break; default: break; } tok_skip(); if (wordfollows) { *wordfollows = parse_word(pipeword?0:PT_WF_GLOBONE); if (*wordfollows == 0) { shp("Missing word after %s\n",tokname(redirtok)); print_tokpt(); pt_free(new); return(0); } if (wordfollows[0]->type != PT_WORD) panic("parse_word returned non-word"); if (pipeword && (wordfollows[0]->u.word.flags & PT_WF_ANYBQ)) { shp("Backquotes not permitted in pipe name after %s\n",tokname(redirtok)); print_tokpt(); pt_free(new); return(0); } } return(new); } static PT *parse_wordlist(unsigned int flags) { PT *wl; PT *w; wl = pt_new(); wl->type = PT_WORDLIST; wl->u.wl.flags = 0; wl->u.wl.n = 0; wl->u.wl.list = 0; while (1) { w = parse_word(flags); if (w == 0) break; if (w->type != PT_WORD) panic("parse_word returned non-word"); wl->u.wl.list = xreget(wl->u.wl.list,(wl->u.wl.n+1)*sizeof(PT *)); wl->u.wl.list[wl->u.wl.n++] = w; if (w->u.word.flags & PT_WF_ANYBQ) wl->u.wl.flags |= PT_WLF_HASBQ; } return(wl); } static void parse_command_simple(PT *pt) { PT *wl; wl = parse_wordlist(PT_WF_GLOBMANY); if (wl->type != PT_WORDLIST) panic("parse_wordlist returned non-wordlist"); if (wl->u.wl.n < 1) { shp("Empty command\n"); print_tokpt(); pt_free(wl); pt_makenone(pt); return; } if (wl->u.wl.list[0]->type != PT_WORD) panic("non-word beginning wordlist"); if (! (wl->u.wl.list[0]->u.word.flags & PT_WF_GLOBMANY)) panic("word flags wrong"); wl->u.wl.list[0]->u.word.flags &= ~PT_WF_GLOBMANY; wl->u.wl.list[0]->u.word.flags |= PT_WF_GLOBONE; pt->u.command.type = PT_CMD_SIMPLE; pt->u.command.u.simple.p = 0; pt->u.command.u.simple.wordlist = wl; } static void parse_command_bracketed(PT *pt, int cmdtype, int termtok) { PT *seq; int opentok; opentok = tok_next(0)->type; tok_skip(); seq = parse_sequence(); if (seq == 0) { shp("Missing sequence after %s\n",tokname(opentok)); print_tokpt(); pt_makenone(pt); return; } if (tok_next(0)->type != termtok) { shp("Missing %s\n",tokname(termtok)); print_tokpt(); pt_free(seq); pt_makenone(pt); return; } tok_skip(); pt->u.command.type = cmdtype; pt->u.command.u.seq = seq; } static void pipename_check(PT *n) { if (n->type != PT_WORD) panic("pipe name isn't a word"); if (n->u.word.flags & PT_WF_ANYBQ) panic("backquote in pipe name"); if (n->u.word.flags & (PT_WF_GLOBONE|PT_WF_GLOBMANY)) panic("glob pipe name"); if (n->u.word.flags & PT_WF_TEXT) panic("pipe name word textified"); if (n->u.word.len != 1) panic("%s pipe name",(n->u.word.len<1)?"empty":"multipart"); if (n->u.word.u.parts[0].type != WPT_TEXT) panic("non-text pipe name"); } static void parse_command_parallel(PT *pt) { __label__ ret; PT *p; int i; auto void fail(const char *, ...) __attribute__((__format__(__printf__,1,2),__noreturn__)); auto void fail(const char *fmt, ...) { va_list ap; va_start(ap,fmt); vshp(fmt,ap); va_end(ap); shp("\n"); print_tokpt(); pt_makenone(pt); goto ret; } pt->u.command.type = PT_CMD_PARALLEL; pt->u.command.u.parallel.name = 0; pt->u.command.u.parallel.n_pipes = 0; pt->u.command.u.parallel.pipes = 0; pt->u.command.u.parallel.n_seq = 0; pt->u.command.u.parallel.seqs = 0; tok_skip(); if ( (tok_next(0)->type == TOK_WORDTEXT) && (tok_next_n(0,1)->type == TOK_PCOLON) ) { pt->u.command.u.parallel.name = parse_word(0); pipename_check(pt->u.command.u.parallel.name); tok_skip(); } p = parse_wordlist(0); if (p->type != PT_WORDLIST) panic("parse_wordlist returned non-wordlist"); if (p->u.wl.flags & PT_WLF_HASBQ) { pt_free(p); fail("Backquotes not permitted in %s %s pipe names",tokname(TOK_PLEFT),tokname(TOK_PRIGHT)); } pt->u.command.u.parallel.n_pipes = p->u.wl.n; pt->u.command.u.parallel.pipes = xget(p->u.wl.n*sizeof(PPIPE)); for (i=p->u.wl.n-1;i>=0;i--) { PT *w; w = p->u.wl.list[i]; pipename_check(w); pt->u.command.u.parallel.pipes[i].name = w; pt->u.command.u.parallel.pipes[i].fds[0] = -1; pt->u.command.u.parallel.pipes[i].fds[1] = -1; pt->u.command.u.parallel.pipes[i].refs[0] = 0; pt->u.command.u.parallel.pipes[i].refs[1] = 0; p->u.wl.list[i] = 0; } pt_free(p); if (tok_next(0)->type != TOK_PMINUS) fail("Missing %s",tokname(TOK_PMINUS)); tok_skip(); while (1) { PT *seq; seq = parse_sequence(); if (seq == 0) fail("Missing sequence"); pt->u.command.u.parallel.seqs = xreget(pt->u.command.u.parallel.seqs,(pt->u.command.u.parallel.n_seq+1)*sizeof(PT *)); pt->u.command.u.parallel.seqs[pt->u.command.u.parallel.n_seq++] = seq; switch (tok_next(0)->type) { case TOK_PSEMI: tok_skip(); continue; break; case TOK_PRIGHT: tok_skip(); return; break; default: break; } fail("Missing %s or %s",tokname(TOK_PSEMI),tokname(TOK_PRIGHT)); } ret:; } static void parse_command_cond(PT *pt) { __label__ ret; PT *seq; auto void fail(const char *, ...) __attribute__((__format__(__printf__,1,2),__noreturn__)); auto void fail(const char *fmt, ...) { va_list ap; va_start(ap,fmt); vshp(fmt,ap); va_end(ap); shp("\n"); print_tokpt(); pt_makenone(pt); goto ret; } pt->u.command.type = PT_CMD_COND; pt->u.command.u.cond.test = 0; pt->u.command.u.cond.ift = 0; pt->u.command.u.cond.iff = 0; tok_skip(); seq = parse_sequence(); if (seq == 0) fail("Missing condition"); pt->u.command.u.cond.test = seq; if (tok_next(0)->type == TOK_CTHEN) { tok_skip(); seq = parse_sequence(); if (seq == 0) fail("Missing %s part",tokname(TOK_CTHEN)); pt->u.command.u.cond.ift = seq; } if (tok_next(0)->type == TOK_CELSE) { tok_skip(); seq = parse_sequence(); if (seq == 0) fail("Missing %s part",tokname(TOK_CELSE)); pt->u.command.u.cond.iff = seq; } if (tok_next(0)->type != TOK_RPAR) fail("Improperly closed %s",tokname(TOK_CIF)); tok_skip(); ret:; } static PT *parse_command(void) { PT *new; PT *r; new = command_new(); while (1) { r = parse_redirection(); if (! r) break; add_redirection(new,r); } switch (tok_next(0)->type) { case TOK_LPAR: parse_command_bracketed(new,PT_CMD_PAREN,TOK_RPAR); break; case TOK_LBANG: { PT *t; parse_command_bracketed(new,PT_CMD_SUBSHELL,TOK_RPAR); t = new->u.command.u.seq; new->u.command.u.subshell.seq = t; new->u.command.u.subshell.state = 0; } break; case TOK_PLEFT: parse_command_parallel(new); break; case TOK_ANDL: parse_command_bracketed(new,PT_CMD_NOWAIT,TOK_ANDR); break; case TOK_ANDANDL: parse_command_bracketed(new,PT_CMD_BG,TOK_ANDR); break; case TOK_CIF: parse_command_cond(new); break; default: parse_command_simple(new); break; } if (new->type == PT_NONE) { pt_free(new); return(0); } while (1) { r = parse_redirection(); if (! r) break; add_redirection(new,r); } return(new); } static PT *parse_pt_list(PT *(*subfn)(void), int type, const char *subname, int septok) { PT *new; PT *sub; new = pt_new(); new->type = type; new->u.pts.n = 0; new->u.pts.list = 0; while (1) { sub = (*subfn)(); if (sub == 0) { shp("Missing %s",subname); if (new->u.pts.n > 0) shp(" after %s",tokname(septok)); shp("\n"); print_tokpt(); pt_free(new); return(0); } new->u.pts.list = xreget(new->u.pts.list,(new->u.pts.n+1)*sizeof(PT *)); new->u.pts.list[new->u.pts.n++] = sub; if (tok_next(0)->type != septok) break; tok_skip(); } return(new); } static PT *parse_pipeline(void) { return(parse_pt_list(parse_command,PT_PIPELINE,"command",TOK_PIPE)); } static PT *parse_andand(void) { return(parse_pt_list(parse_pipeline,PT_ANDAND,"pipeline",TOK_ANDAND)); } static PT *parse_oror(void) { static char *s = 0; if (! s) s = xaprintf("%s list",tokname(TOK_ANDAND)); return(parse_pt_list(parse_andand,PT_OROR,s,TOK_OROR)); } static PT *parse_sequence(void) { static char *s = 0; if (! s) s = xaprintf("%s list",tokname(TOK_OROR)); return(parse_pt_list(parse_oror,PT_SEQUENCE,s,TOK_SEMI)); } static PT *parse_command_line(void) { PT *new; PT *seq; if (tok_next(0)->type == TOK_EOL) return(0); seq = parse_sequence(); if (seq == 0) { shp("Missing sequence\n"); print_tokpt(); return(0); } new = pt_new(); new->type = PT_CMDLINE; new->u.cmdline.ntok = 0; new->u.cmdline.toks = 0; new->u.cmdline.seq = seq; new->u.cmdline.bg = 0; if (tok_next(0)->type == TOK_AND) { tok_skip(); new->u.cmdline.bg = 1; } if (tok_next(0)->type != TOK_EOL) { shp("Trailing junk on command line\n"); print_tokpt(); pt_free(new); return(0); } return(new); } static void print_oflags(int, FILE *) __attribute__((__unused__)); static void print_oflags(int oflags, FILE *f) { static struct { int bit; const char *name; } bits[] = { { O_APPEND, "O_APPEND" }, { O_CREAT, "O_CREAT" }, { O_TRUNC, "O_TRUNC" }, { 0, 0 } }; int i; switch (oflags & (O_RDONLY|O_RDWR|O_WRONLY)) { case O_RDONLY: fprintf(f,"O_RDONLY"); break; case O_WRONLY: fprintf(f,"O_WRONLY"); break; case O_RDWR: fprintf(f,"O_RDWR"); break; default: fprintf(f,"(?%d)",oflags&(O_RDONLY|O_RDWR|O_WRONLY)); break; } oflags &= ~(O_RDONLY|O_RDWR|O_WRONLY); for (i=0;bits[i].name;i++) { if (oflags & bits[i].bit) { fprintf(f,"|%s",bits[i].name); oflags &= ~bits[i].bit; } } if (oflags) fprintf(f,"|%x",oflags); } #define PT_ILW 2 /* indent level width - chars per indent level */ /* This is uuuugly. We want gcc format checking when we give a format, but we also want to be able to specify some sort of nil format in a way that won't produce "null format string" (which is what we get for a nil pointer) or "zero-length format string" (which is what we get for "" - why?!). This makes NOTAG a format string that gcc doesn't try to check. */ #define NOTAG (&pt_dump_notag) static const char pt_dump_notag; static void pt_dump(PT *, int, const char *, ...) __attribute__((__format__(__printf__,3,4),__unused__)); static void pt_dump(PT *pt, int indent, const char *fmt, ...) { int i; va_list ap; FDENV *e; shp("%*s",indent*PT_ILW,""); if (fmt != NOTAG) { va_start(ap,fmt); vshp(fmt,ap); va_end(ap); shp(" = "); } if (pt == 0) { shp("(nil)\n"); return; } shp("[%p %c%c%c%c] ",(void *)pt, (pt->flags & PTF_STARTED) ? 'T' : 't', (pt->flags & PTF_DONE ) ? 'D' : 'd', (pt->flags & PTF_PRUNE ) ? 'P' : 'p', (pt->flags & PTF_SUCCESS) ? 'S' : 's' ); switch (pt->type) { default: shp("?%d\n",pt->type); break; case PT_NONE: shp("NONE\n"); break; case PT_CMDLINE: shp("CMDLINE, bg=%d\n",pt->u.cmdline.bg); pt_dump(pt->u.cmdline.seq,indent+1,NOTAG); break; case PT_COMMAND: shp("COMMAND: "); switch (pt->u.command.type) { default: shp("?%d\n",pt->u.command.type); break; case PT_CMD_NONE: shp("NONE\n"); break; case PT_CMD_SIMPLE: shp("SIMPLE\n"); break; case PT_CMD_PAREN: shp("PAREN\n"); break; case PT_CMD_SUBSHELL: shp("SUBSHELL\n"); break; case PT_CMD_PARALLEL: shp("PARALLEL, n_pipes %d, n_seq %d\n",pt->u.command.u.parallel.n_pipes,pt->u.command.u.parallel.n_seq); pt_dump(pt->u.command.u.parallel.name,indent+1,"name"); for (i=0;iu.command.u.parallel.n_pipes;i++) { pt_dump(pt->u.command.u.parallel.pipes[i].name,indent+1,"pipe #%d refs (%d,%d) fds (%d,%d) name",i,pt->u.command.u.parallel.pipes[i].refs[0],pt->u.command.u.parallel.pipes[i].refs[1],pt->u.command.u.parallel.pipes[i].fds[0],pt->u.command.u.parallel.pipes[i].fds[1]); } break; case PT_CMD_NOWAIT: shp("NOWAIT\n"); break; case PT_CMD_BG: shp("BG\n"); break; } for (i=0;iu.command.n_redir;i++) pt_dump(pt->u.command.redirs[i],indent+1,"redir #%d",i); e = pt->u.command.env; if (e) { shp("%*senv =\n",(indent+1)*PT_ILW,""); for (i=0;in;i++) { shp("%*s%d",(indent+2)*PT_ILW,"",e->list[i].fd); switch (e->list[i].type) { case FDFD_NONE: shp(" NONE\n"); break; case FDFD_CLOSE: shp(" CLOSE\n"); break; case FDFD_SHELL: shp(" SHELL %d\n",e->list[i].u.shfd); break; case FDFD_FILE: shp(" FILE %p\n",(void *)e->list[i].u.redir); break; case FDFD_PIPE: shp(" PIPE %p\n",(void *)e->list[i].u.redir); break; case FDFD_BQOUT: shp(" BQOUT %p\n",(void *)e->list[i].u.bq); break; default: shp(" ?%d\n",e->list[i].type); break; } } } else { shp("%*senv = (nil)\n",(indent+1)*PT_ILW,""); } switch (pt->u.command.type) { default: panic("impossible pt type"); break; case PT_CMD_NONE: break; case PT_CMD_SIMPLE: pt_dump(pt->u.command.u.simple.wordlist,indent+1,NOTAG); break; case PT_CMD_PAREN: pt_dump(pt->u.command.u.seq,indent+1,NOTAG); break; case PT_CMD_SUBSHELL: pt_dump(pt->u.command.u.subshell.seq,indent+1,NOTAG); shp("%*sstate = %p\n",(indent+1)*PT_ILW,"",(void *)pt->u.command.u.subshell.state); break; case PT_CMD_PARALLEL: for (i=0;iu.command.u.parallel.n_seq;i++) { pt_dump(pt->u.command.u.parallel.seqs[i],indent+1,"seq #%d",i); } break; case PT_CMD_NOWAIT: pt_dump(pt->u.command.u.seq,indent+1,NOTAG); break; case PT_CMD_BG: pt_dump(pt->u.command.u.seq,indent+1,NOTAG); break; case PT_CMD_COND: shp("(unimplemented)\n"); break; } break; case PT_WORDLIST: shp("WORDLIST: flags "); if (pt->u.wl.flags == 0) shp("none"); if (pt->u.wl.flags & PT_WLF_HASBQ) shp("`"); if (pt->u.wl.flags & ~PT_WLF__ALL) shp("<%x>",pt->u.wl.flags&~PT_WLF__ALL); shp("\n"); for (i=0;iu.wl.n;i++) pt_dump(pt->u.wl.list[i],indent+1,NOTAG); break; case PT_SEQUENCE: shp("SEQUENCE\n"); for (i=0;iu.pts.n;i++) pt_dump(pt->u.pts.list[i],indent+1,NOTAG); break; case PT_OROR: shp("OROR\n"); for (i=0;iu.pts.n;i++) pt_dump(pt->u.pts.list[i],indent+1,NOTAG); break; case PT_ANDAND: shp("ANDAND\n"); for (i=0;iu.pts.n;i++) pt_dump(pt->u.pts.list[i],indent+1,NOTAG); break; case PT_PIPELINE: shp("PIPELINE\n"); for (i=0;iu.pts.n;i++) pt_dump(pt->u.pts.list[i],indent+1,NOTAG); break; case PT_REDIR: shp("REDIR (ref %d use %d): ",pt->u.redir.refcnt,pt->u.redir.usecnt); switch (pt->u.redir.type) { default: shp("?%d\n",pt->u.redir.type); break; case PT_REDIR_NONE: shp("NONE\n"); break; case PT_REDIR_FILE: shp("FILE: n %d, oflags=",pt->u.redir.n); print_oflags(pt->u.redir.u.file.oflags,shof); shp(" (shfd %d)\n",pt->u.redir.u.file.shfd); pt_dump(pt->u.redir.u.file.word,indent+1,"file"); break; case PT_REDIR_FILEAND: shp("FILEAND, oflags="); print_oflags(pt->u.redir.u.fileand.oflags,shof); shp("\n"); pt_dump(pt->u.redir.u.fileand.word,indent+1,"file"); break; case PT_REDIR_DUP: shp("DUP: n %d, m ",pt->u.redir.n); if (DUP_SPECIAL(pt->u.redir.u.dup.m)) { switch (pt->u.redir.u.dup.m) { case DUP_CLOSE: shp("CLOSE"); break; default: shp("?%d?",pt->u.redir.u.dup.m); break; } } else { shp("%d",pt->u.redir.u.dup.m); } shp("\n"); break; case PT_REDIR_PIPE: shp("PIPE"); shp(": n %d which %d\n",pt->u.redir.n,pt->u.redir.u.pipe.which); pt_dump(pt->u.redir.u.pipe.word,indent+1,"pipe name"); if (pt->u.redir.u.pipe.parallel) { shp("%*s(parallel=%p pipeno=",(indent+1)*PT_ILW,"",(void *)pt->u.redir.u.pipe.parallel); if (PIPENO_SPECIAL(pt->u.redir.u.pipe.pipeno)) { switch (pt->u.redir.u.pipe.pipeno) { case PIPENO_COPY: shp("COPY"); break; default: shp("?%d?",pt->u.redir.u.pipe.pipeno); break; } } else { shp("%d",pt->u.redir.u.pipe.pipeno); } shp(")\n"); } break; } break; case PT_WORD: shp("WORD: flags "); if (pt->u.word.flags == 0) shp("none"); if (pt->u.word.flags & PT_WF_GLOBONE) shp("1"); if (pt->u.word.flags & PT_WF_GLOBMANY) shp("*"); if (pt->u.word.flags & PT_WF_ANYBQ) shp("`"); if (pt->u.word.flags & ~PT_WF__ALL) shp("<%x>",pt->u.word.flags&~PT_WF__ALL); if (pt->u.word.flags & PT_WF_TEXT) { shp(" len=%d text=%s\n",pt->u.word.len,pt->u.word.u.text); } else { shp(" len=%d\n",pt->u.word.len); for (i=0;iu.word.len;i++) { switch (pt->u.word.u.parts[i].type) { case WPT_TEXT: { TOKEN *t; t = pt->u.word.u.parts[i].u.text; shp("%*stext = ",(indent+1)*PT_ILW,""); fwrite(t->u.w.s,1,t->u.w.l,shof); shp("\n"); } break; case WPT_BQ: pt_dump(pt->u.word.u.parts[i].u.bq,indent+1,"backquoted command"); break; default: shp("%*s?type=%d\n",(indent+1)*PT_ILW,"",pt->u.word.u.parts[i].type); break; } } } break; case PT_BQ: shp("BQ: flags "); if (pt->u.bq.bqflags == 0) shp("none"); if (pt->u.bq.bqflags & PT_BQF_Q_ALL) shp("q"); if (pt->u.bq.bqflags & PT_BQF_NEWLINE) shp("n"); if (pt->u.bq.bqflags & PT_BQF_Z_LEAD) shp("l"); if (pt->u.bq.bqflags & PT_BQF_Z_TRAIL) shp("t"); if (pt->u.bq.bqflags & PT_BQF_Z_OTHER) shp("z"); if (pt->u.bq.bqflags & PT_BQF_ERROR) shp("e"); if (pt->u.bq.bqflags & ~PT_BQF__BQALL) shp("<%x>",pt->u.bq.bqflags&~PT_BQF__BQALL); shp(" / "); if (pt->u.bq.rtflags == 0) shp("none"); if (pt->u.bq.rtflags & PT_BQF_HAVE_R) shp("r(%d)",pt->u.bq.pipe[0]); if (pt->u.bq.rtflags & PT_BQF_HAVE_W) shp("w(%d)",pt->u.bq.pipe[1]); if (pt->u.bq.rtflags & PT_BQF_GOTEOF) shp("e"); if (pt->u.bq.rtflags & PT_BQF_LISTED) shp("l"); if (pt->u.bq.rtflags & ~PT_BQF__RTALL) shp("<%x>",pt->u.bq.rtflags&~PT_BQF__RTALL); shp("\n"); pt_dump(pt->u.bq.seq,indent+1,NOTAG); break; } } static __inline__ int es_success(EXITSTAT es) { return((es.type==ET_EXIT)&&(es.u.exit==0)); } static BUILTIN child_builtins[] = { /*{ "echo", builtin_echo }, { "which", builtin_which },*/ { 0 } }; static int bivec_find(BUILTIN *list, int len, const char *name) { int i; int j; if (list[0].namelen == 0) { for (i=0;list[i].name;i++) list[i].namelen = strlen(list[i].name); } for <"nextbi"> (i=0;list[i].name;i++) { j = list[i].namelen; if (len != j) continue; for (j--;j>=0;j--) if (name[j] != list[i].name[j]) continue <"nextbi">; return(i); } return(-1); } static void child_builtin(PT *pt) { PT *w; int i; if (pt->type != PT_WORDLIST) panic("bad type"); if (pt->u.wl.n < 1) panic("empty command"); w = pt->u.wl.list[0]; if (w->type != PT_WORD) panic("non-word"); if (w->u.word.flags & PT_WF_ANYBQ) return; if (! (w->u.word.flags & PT_WF_TEXT)) panic("no word text"); i = bivec_find(&child_builtins[0],w->u.word.len,w->u.word.u.text); if (i < 0) return; (*child_builtins[i].fn)(pt); exit(0); } static int fork_for(JOB *j, PT *p) { pid_t kid; JOBKID *k; if ( (p->type != PT_COMMAND) || (p->u.command.type != PT_CMD_SIMPLE) ) panic("simple command isn't"); if (p->u.command.u.simple.p) panic("multiple forking"); if (! (j->flags & JF_HAVEPIPE)) { if (pipe(&j->gopipe[0]) < 0) { shp("pipe: %s\n",strerror(errno)); return(-1); } j->flags |= JF_HAVEPIPE; } shp("[gopipe %d %d]\n",j->gopipe[0],j->gopipe[1]); shfd_add(j->gopipe[0]); shfd_add(j->gopipe[1]); kid = fork(); if (kid < 0) { shp("fork: %s\n",strerror(errno)); return(-1); } if (kid == 0) return(0); if (j->flags & JF_HAVEPG) { if (setpgid(kid,j->pg) < 0) { shp("setpgid(%d,%d): %s\n",(int)kid,(int)j->pg,strerror(errno)); kill(kid,SIGKILL); return(-1); } } else { int p[2]; pid_t pk; if (setpgid(kid,kid) < 0) { shp("setpgid(%d,%d): %s\n",(int)kid,(int)kid,strerror(errno)); kill(kid,SIGKILL); return(-1); } j->flags |= JF_HAVEPG; j->pg = kid; if (j->flags & JF_HAVEPROC) shfd_close(j->procpipe); if (pipe(&p[0]) < 0) { shp("pipe: %s\n",strerror(errno)); return(-1); } shp("[procpipe %d %d]\n",p[0],p[1]); shfd_add(p[0]); shfd_add(p[1]); pk = fork(); if (pk == 0) { int f; f = dup(p[0]); shfd_closeall(); read(f,&p[1],1); exit(0); } shp("[job proc %d]\n",(int)pk); shfd_close(p[0]); if (setpgid(pk,j->pg) < 0) { shp("setpgid(%d,%d): %s\n",(int)pk,(int)j->pg,strerror(errno)); kill(pk,SIGKILL); kill(kid,SIGKILL); shfd_close(p[1]); return(-1); } j->procpipe = p[1]; j->proc = pk; j->flags |= JF_HAVEPROC; } k = xget(sizeof(JOBKID)); k->link = j->livekids; j->livekids = k; k->j = j; k->pid = kid; k->pt = p; p->u.command.u.simple.p = k; return(1); } static JOB *new_job(void) { JOB *j; j = xget(sizeof(JOB)); j->flags = 0; j->livekids = 0; j->flink = jobhead; j->blink = 0; if (jobhead) jobhead->blink = j; else jobtail = j; jobhead = j; return(j); } static void free_job(JOB *j) { while (j->livekids) { JOBKID *k; k = j->livekids; shp("Warning: job %p has kid %p (pid %d)\n",(void *)j,(void *)k,(int)k->pid); j->livekids = k->link; if (k->pt) { if ( (k->pt->type != PT_COMMAND) || (k->pt->u.command.type != PT_CMD_SIMPLE) ) panic("kid's simple command isn't"); if (k->pt->u.command.u.simple.p != k) panic("kid backpointer wrong"); k->pt->u.command.u.simple.p = 0; } xput(k); } if (j->flink) j->flink->blink = j->blink; else jobtail = j->blink; if (j->blink) j->blink->flink = j->flink; else jobhead = j->flink; if (j->flags & JF_HAVEPROC) close(j->procpipe); xput(j); } static void pt_walk(PT *p, void (*pre)(PT *), void (*post)(PT *)) { static void walk(PT *p) { int i; if (! p) return; if (pre) (*pre)(p); switch (p->type) { default: panic("pt_walk: unknown type %d",p->type); break; case PT_NONE: break; case PT_CMDLINE: walk(p->u.cmdline.seq); break; case PT_COMMAND: for (i=0;iu.command.n_redir;i++) walk(p->u.command.redirs[i]); switch (p->u.command.type) { default: panic("pt_walk: unknown command type %d",p->u.command.type); break; case PT_CMD_NONE: break; case PT_CMD_SIMPLE: walk(p->u.command.u.simple.wordlist); break; case PT_CMD_PAREN: case PT_CMD_NOWAIT: case PT_CMD_BG: walk(p->u.command.u.seq); break; case PT_CMD_SUBSHELL: walk(p->u.command.u.subshell.seq); break; case PT_CMD_PARALLEL: walk(p->u.command.u.parallel.name); for (i=0;iu.command.u.parallel.n_pipes;i++) walk(p->u.command.u.parallel.pipes[i].name); for (i=0;iu.command.u.parallel.n_seq;i++) walk(p->u.command.u.parallel.seqs[i]); break; } break; case PT_WORDLIST: for (i=0;iu.wl.n;i++) walk(p->u.wl.list[i]); break; case PT_SEQUENCE: case PT_OROR: case PT_ANDAND: case PT_PIPELINE: for (i=0;iu.pts.n;i++) walk(p->u.pts.list[i]); break; case PT_REDIR: switch (p->u.redir.type) { default: panic("pt_walk: unknown redir type %d",p->u.redir.type); break; case PT_REDIR_NONE: break; case PT_REDIR_FILE: walk(p->u.redir.u.file.word); break; case PT_REDIR_FILEAND: walk(p->u.redir.u.fileand.word); break; case PT_REDIR_DUP: break; case PT_REDIR_PIPE: walk(p->u.redir.u.pipe.word); break; } break; case PT_WORD: if (! (p->u.word.flags & PT_WF_TEXT)) { for (i=0;iu.word.len;i++) { WORDPART *wp; wp = &p->u.word.u.parts[i]; switch (wp->type) { default: break; case WPT_BQ: walk(wp->u.bq); break; } } } break; case PT_BQ: walk(p->u.bq.seq); break; } if (post) (*post)(p); } walk(p); } static void deref_simple_envs(PT *p) { if ( (p->type == PT_COMMAND) && (p->u.command.type == PT_CMD_SIMPLE) && p->u.command.env ) { fdenv_deref(p->u.command.env); p->u.command.env = 0; } } /* If the sub-command isn't done, do nothing. Else, we want to propagate doneness and pruneness upwards. If this involves setting the DONE bit, also copy the SUCCESS bit's state upwards at the same time. Return true iff we actually change p's flags. This definition is carefully designed to DTRT for NOWAIT and BG. */ static int donecopy(PT *p, PT *sub) { unsigned int f; if (! (sub->flags & PTF_DONE)) return(0); f = p->flags; if ((sub->flags & PTF_DONE) && !(f & PTF_DONE)) { f = (f & ~PTF_SUCCESS) | (sub->flags & PTF_SUCCESS) | PTF_DONE; } if (sub->flags & PTF_PRUNE) f |= PTF_PRUNE; if (f != p->flags) { p->flags = f; if (f & PTF_DONE) pt_walk(p,0,&deref_simple_envs); return(1); } return(0); } static void setdone(PT *p, int success) { p->flags |= PTF_DONE | PTF_PRUNE; if (success) p->flags |= PTF_SUCCESS; else p->flags &= ~PTF_SUCCESS; pt_walk(p,0,&deref_simple_envs); } static int startcopy(PT *p, PT *sub) { if ( !(sub->flags & PTF_STARTED) || (p->flags & PTF_STARTED) ) return(0); p->flags |= PTF_STARTED; pt_walk(p,0,&deref_simple_envs); return(1); } static void setstarted(PT *p) { p->flags |= PTF_STARTED; pt_walk(p,0,&deref_simple_envs); } static void word_text_0(PT *p) { int i; int len; WORDPART *wp; TOKEN *t; char *buf; int o; if (p == 0) return; if (p->type != PT_WORD) panic("word isn't"); if (! (p->flags & PTF_DONE)) return; if (p->u.word.flags & PT_WF_TEXT) return; if (p->u.word.flags & PT_WF_ANYBQ) panic("impossible backquote"); len = 0; for (i=0;iu.word.len;i++) { wp = &p->u.word.u.parts[i]; switch (wp->type) { case WPT_TEXT: t = wp->u.text; if (t->type != TOK_WORDTEXT) panic("non-wordtext token in word"); len += t->u.w.l; break; default: panic("impossible token type"); break; } } buf = xget(len+1); o = 0; for (i=0;iu.word.len;i++) { t = p->u.word.u.parts[i].u.text; bcopy(t->u.w.s,buf+o,t->u.w.l); o += t->u.w.l; } if (o != len) panic("length mismatch"); buf[o] = '\0'; for (i=p->u.word.len-1;i>=0;i--) free_wordpart_sub(&p->u.word.u.parts[i]); xput(p->u.word.u.parts); p->u.word.flags |= PT_WF_TEXT; p->u.word.len = len; p->u.word.u.text = buf; } static int bq_sep(unsigned int flags, unsigned char ch) { if (flags & PT_BQF_Q_ALL) return(0); if (flags & PT_BQF_NEWLINE) return(ch=='\n'); return(ct_whitespace(ch)); } static void expand_bq_word(PT *w, int *np, PT ***listp) { PT **list; int nlist; int alist; char *buf; int buflen; int bufalloc; int i; WORDPART *wp; PT *b; int s; int e; static void provide_text(const char *data, int len) { if (buflen < 0) buflen = 0; if (buflen+len > bufalloc) buf = xreget(buf,bufalloc=buflen+len); bcopy(data,buf+buflen,len); buflen += len; } static void provide_break(void) { PT *new; new = pt_new(); new->type = PT_WORD; new->flags = PTF_DONE | PTF_SUCCESS; new->u.word.flags = PT_WF_TEXT | PT_WF_ANYBQ; new->u.word.len = buflen; new->u.word.u.text = xget(buflen+1); bcopy(buf,new->u.word.u.text,buflen); new->u.word.u.text[buflen] = '\0'; if (nlist >= alist) list = xreget(list,(alist=nlist+8)*sizeof(*list)); list[nlist++] = new; buflen = -1; } list = 0; nlist = 0; alist = 0; buf = 0; buflen = -1; if ( (w->type != PT_WORD) || (w->u.word.flags & PT_WF_TEXT) || !(w->u.word.flags & PT_WF_ANYBQ) ) panic("impossible expand_bq_word"); for (i=0;iu.word.len;i++) { wp = &w->u.word.u.parts[i]; switch (wp->type) { default: panic("impossible wordpart type"); break; case WPT_TEXT: if (wp->u.text->type != TOK_WORDTEXT) panic("non-wordtext text word part"); provide_text(wp->u.text->u.w.s,wp->u.text->u.w.l); break; case WPT_BQ: b = wp->u.bq; if (b->type != PT_BQ) panic("backquote type wrong"); if (! (b->flags & PTF_DONE)) panic("undone backquote"); s = 0; for (e=0;eu.bq.len;e++) { if (bq_sep(b->u.bq.bqflags,b->u.bq.buf[e])) { if ( (s != e) || (b->u.bq.bqflags & (e ? PT_BQF_Z_LEAD : PT_BQF_Z_OTHER)) ) { provide_text(b->u.bq.buf+s,e-s); provide_break(); } s = e + 1; } } if ((s != e) || (b->u.bq.bqflags & PT_BQF_Z_TRAIL)) { provide_text(b->u.bq.buf+s,e-s); } break; } } if (buflen >= 0) provide_break(); xput(buf); *np = nlist; *listp = list; } static void word_text_list(PT *wl) { int i; PT *w; int n; PT **list; int alloc; if (wl->type != PT_WORDLIST) panic("wordlist isn't"); if (! (wl->u.wl.flags & PT_WLF_HASBQ)) { for (i=wl->u.wl.n-1;i>=0;i--) word_text_0(wl->u.wl.list[i]); return; } alloc = wl->u.wl.n; for (i=wl->u.wl.n-1;i>=0;i--) { w = wl->u.wl.list[i]; if (w->type != PT_WORD) panic("wordlist's word isn't"); if (! (w->flags & PTF_DONE)) panic("word not done"); if (! (w->u.word.flags & PT_WF_ANYBQ)) { word_text_0(w); continue; } if (w->u.word.flags & PT_WF_TEXT) panic("word already has text"); expand_bq_word(w,&n,&list); switch (n) { case 0: wl->u.wl.n --; if (i < wl->u.wl.n) bcopy(&wl->u.wl.list[i+1],&wl->u.wl.list[i],(wl->u.wl.n-i)*sizeof(*wl->u.wl.list)); break; case 1: wl->u.wl.list[i] = list[0]; break; default: if (wl->u.wl.n+n-1 > alloc) { alloc = wl->u.wl.n + n - 1; wl->u.wl.list = xreget(wl->u.wl.list,alloc*sizeof(*wl->u.wl.list)); } if (i < wl->u.wl.n-1) bcopy(wl->u.wl.list+i+1,wl->u.wl.list+n,(wl->u.wl.n-1-i)*sizeof(*wl->u.wl.list)); bcopy(list,wl->u.wl.list+i,n*sizeof(*list)); break; } pt_free(w); xput(list); } } static int word_text_1(PT *w, const char *tag) { int n; PT **list; if (w->type != PT_WORD) panic("word isn't"); if (w->u.word.flags & PT_WF_TEXT) panic("word has text"); if (w->u.word.flags & PT_WF_ANYBQ) { expand_bq_word(w,&n,&list); if (n != 1) { shp("Multi-word backquote expansion for %s\n",tag); for (n--;n>=0;n--) pt_free(list[n]); xput(list); return(1); } pt_makenone(w); *w = *list[0]; pt_justfree(list[0]); xput(list); } else { word_text_0(w); } return(0); } /* Note that where this code says "any |= foo(); any |= bar();" type things, it won't do to write "any |= foo() | bar();" instead, because there needs to be a sequence point between foo() and bar(), to enforce ordering. */ static int check_done(PT *p) { PT *p2; unsigned int f; int i; int any; #define CHECKLOOP(N,SUBV) do {\ for (i=(N)-1;i>=0;i--) any |= check_done((SUBV)[i]); \ for (i=(N)-1;i>=0;i--) if (! ((SUBV)[i]->flags & PTF_DONE)) return(any);\ for (i=(N)-1;i>=0;i--) \ { if (! ((SUBV)[i]->flags & PTF_SUCCESS)) \ { setdone(p,0); \ return(1); \ } \ } \ } while (0) if (p == 0) return(0); if (p->flags & PTF_PRUNE) { if (! (p->flags & PTF_DONE)) panic("pruned but not done"); return(0); } any = 0; switch (p->type) { default: panic("check_done bad type %u\n",p->type); break; case PT_CMDLINE: any |= check_done(p->u.cmdline.seq); any |= donecopy(p,p->u.cmdline.seq); break; case PT_COMMAND: #define C p->u.command CHECKLOOP(C.n_redir,C.redirs); switch (C.type) { default: panic("check_done bad command type %u\n",p->u.command.type); break; case PT_CMD_SIMPLE: any |= check_done(C.u.simple.wordlist); /* A simple command's doneness is not just propagated upwards from its wordlist; it's set on process death. */ break; case PT_CMD_PAREN: any |= check_done(C.u.seq); any |= donecopy(p,C.u.seq); break; case PT_CMD_SUBSHELL: any |= check_done(C.u.subshell.seq); any |= donecopy(p,C.u.subshell.seq); break; case PT_CMD_PARALLEL: #define P C.u.parallel if (P.n_seq < 1) panic("empty parallel"); any |= check_done(P.name); for (i=P.n_pipes-1;i>=0;i--) any |= check_done(P.pipes[i].name); for (i=P.n_seq-1;i>=0;i--) any |= check_done(P.seqs[i]); word_text_0(P.name); for (i=P.n_pipes-1;i>=0;i--) word_text_0(P.pipes[i].name); if (P.name && !(P.name->flags & PTF_DONE)) return(any); for (i=P.n_pipes-1;i>=0;i--) if (P.pipes[i].name && !(P.pipes[i].name->flags & PTF_DONE)) return(any); for (i=P.n_seq-1;i>=0;i--) if (! (P.seqs[i]->flags & PTF_DONE)) return(any); if (P.name && !(P.name->flags & PTF_SUCCESS)) { setdone(p,0); return(1); } for (i=P.n_pipes-1;i>=0;i--) { if (P.pipes[i].name && !(P.pipes[i].name->flags & PTF_SUCCESS)) { setdone(p,0); return(1); } } for (i=P.n_seq-1;i>=0;i--) { if (! (P.seqs[i]->flags & PTF_SUCCESS)) { setdone(p,0); return(1); } } #undef P setdone(p,1); return(1); break; case PT_CMD_NOWAIT: case PT_CMD_BG: any |= check_done(C.u.seq); any |= donecopy(p,C.u.seq); break; } #undef C break; case PT_WORDLIST: CHECKLOOP(p->u.wl.n,p->u.wl.list); word_text_list(p); setdone(p,1); return(1); break; case PT_SEQUENCE: if (p->u.pts.n < 1) panic("empty sequence"); for (i=0;iu.pts.n;i++) { p2 = p->u.pts.list[i]; any |= check_done(p2); if (! (p2->flags & PTF_DONE)) return(any); } any |= donecopy(p,p->u.pts.list[p->u.pts.n-1]); break; case PT_OROR: if (p->u.pts.n < 1) panic("empty oror"); for (i=0;iu.pts.n;i++) { p2 = p->u.pts.list[i]; any |= check_done(p2); if (! (p2->flags & PTF_DONE)) return(any); if (p2->flags & PTF_SUCCESS) { setdone(p,1); return(1); } } setdone(p,0); return(1); break; case PT_ANDAND: if (p->u.pts.n < 1) panic("empty andand"); for (i=0;iu.pts.n;i++) { p2 = p->u.pts.list[i]; any |= check_done(p2); if (! (p2->flags & PTF_DONE)) return(any); if (! (p2->flags & PTF_SUCCESS)) { setdone(p,0); return(1); } } setdone(p,1); return(1); break; case PT_PIPELINE: panic("check_done pipeline"); #if 0 if (p->u.pts.n < 1) panic("empty pipeline"); for (i=0;iu.pts.n;i++) any |= check_done(p->u.pts.list[i]); for (i=0;iu.pts.n;i++) if (! (p->u.pts.list[i]->flags & PTF_DONE)) return(any); any |= donecopy(p,p->u.pts.list[p->u.pts.n-1]); #endif break; case PT_REDIR: /* Redirs are never set done here; they are set done when they are `started'. See start_it. */ switch (p->u.redir.type) { default: panic("check_done bad redir type %u\n",p->u.redir.type); break; case PT_REDIR_FILE: any |= check_done(p->u.redir.u.file.word); if (word_text_1(p->u.redir.u.file.word,"redirection filename")) { setdone(p,0); return(1); } break; case PT_REDIR_FILEAND: any |= check_done(p->u.redir.u.fileand.word); if (word_text_1(p->u.redir.u.file.word,"redirection filename")) { setdone(p,0); return(1); } break; case PT_REDIR_DUP: break; case PT_REDIR_PIPE: any |= check_done(p->u.redir.u.pipe.word); word_text_0(p->u.redir.u.pipe.word); break; } break; case PT_WORD: if (p->flags & PTF_DONE) panic("done word unpruned"); if (p->u.word.flags & PT_WF_ANYBQ) { f = PTF_DONE | PTF_SUCCESS; for (i=p->u.word.len-1;i>=0;i--) { WORDPART *wp; wp = &p->u.word.u.parts[i]; switch (wp->type) { default: panic("check_done bad wordpart type %u",wp->type); break; case WPT_TEXT: break; case WPT_BQ: p2 = wp->u.bq; any |= check_done(p2); f &= p2->flags; break; } } if (! (f & PTF_DONE)) return(any); if (! (f & PTF_SUCCESS)) { setdone(p,0); return(1); } } setdone(p,1); return(1); break; case PT_BQ: any |= check_done(p->u.bq.seq); if(! (p->u.bq.rtflags & PT_BQF_GOTEOF)) return(any); if (p->u.bq.bqflags & PT_BQF_ERROR) { any |= donecopy(p,p->u.bq.seq); } else { if (p->u.bq.seq->flags & ~p->flags & PTF_DONE) { p->flags |= PTF_DONE | PTF_SUCCESS; any = 1; } if (p->u.bq.seq->flags & ~p->flags & PTF_PRUNE) { p->flags |= PTF_PRUNE; any = 1; } } break; } return(any); #undef CHECKLOOP } static void check_deaths(void) { pid_t kid; int status; JOB *j; JOBKID *k; JOBKID **kp; while <"wait"> (1) { kid = wait4(-1,&status,WNOHANG|WUNTRACED,0); if (kid == 0) return; if (kid < 0) { switch (errno) { case ECHILD: return; break; case EINTR: continue; break; } shp("wait4: %s\n",strerror(errno)); return; } for (j=jobhead;j;j=j->flink) { if ((j->flags & JF_HAVEPROC) && (j->proc == kid)) { j->flags &= ~JF_HAVEPROC; close(j->procpipe); break; } for (kp=&j->livekids;(k=*kp);) { if (k->pid == kid) { if (WIFEXITED(status)) { k->state = JKS_DEAD; k->u.x = (EXITSTAT){ .type=ET_EXIT, .u={ .exit=WEXITSTATUS(status) }}; } else if (WIFSIGNALED(status)) { k->state = JKS_DEAD; k->u.x = (EXITSTAT){ .type=ET_SIG, .u={ .sig={ .sig=WTERMSIG(status), .core=WCOREDUMP(status)?1:0 }}}; } else if (WIFSTOPPED(status)) { k->state = JKS_STOP; k->u.s = WSTOPSIG(status); } else { shp("Warning: pid %d: undecodable status %#x\n",(int)kid,status); } shp("[wait returned pid %d, kid %p, pt %p]\n",(int)kid,(void *)k,(void *)k->pt); if (k->state == JKS_DEAD) { if (k->pt) { shp("[dead, has pt, setting done]\n"); if ( (k->pt->type != PT_COMMAND) || (k->pt->u.command.type != PT_CMD_SIMPLE) ) panic("kid's simple command isn't"); if (k->pt->u.command.u.simple.p != k) panic("kid backpointer wrong"); setdone(k->pt,es_success(k->u.x)); k->pt->u.command.u.simple.p = 0; } else { shp("[dead, no pt]\n"); } *kp = k->link; xput(k); } else { shp("[not dead]\n"); } shp("[continuing]\n"); continue <"wait">; } kp = &k->link; } } } } static void env_merge(FDENV *env, FDFD *new) { int i; FDFD *f; do <"found"> { for (i=env->n-1;i>=0;i--) { if (env->list[i].fd == new->fd) { f = &env->list[i]; break <"found">; } } if (env->n >= env->a) env->list = xreget(env->list,(env->a=env->n+4)*sizeof(*env->list)); f = &env->list[env->n++]; } while (0); *f = *new; } static FDFD *fd_in_env(FDENV *env, int fd) { int i; for (i=env->n-1;i>=0;i--) if (env->list[i].fd == fd) return(&env->list[i]); return(0); } static void apply_redirects(PT *p, FDENV *env) { FDFD new; int i; PT *r; switch (p->type) { case PT_COMMAND: for (i=0;iu.command.n_redir;i++) { r = p->u.command.redirs[i]; if (r->type != PT_REDIR) panic("redir isn't"); switch (r->u.redir.type) { default: panic("bad redir type"); break; case PT_REDIR_FILE: new.type = FDFD_FILE; new.u.redir = r; break; case PT_REDIR_PIPE: new.type = FDFD_PIPE; new.u.redir = r; break; case PT_REDIR_DUP: if (DUP_SPECIAL(r->u.redir.u.dup.m)) { switch (r->u.redir.u.dup.m) { case DUP_CLOSE: new.type = FDFD_CLOSE; break; default: panic("bad special dup"); break; } } else { FDFD *t; t = fd_in_env(env,r->u.redir.u.dup.m); if (t) { new = *t; } else { new.type = FDFD_SHELL; new.u.shfd = r->u.redir.u.dup.m; } } break; } new.fd = r->u.redir.n; env_merge(env,&new); } break; case PT_BQ: new.type = FDFD_BQOUT; new.fd = 1; new.u.bq = p; env_merge(env,&new); break; default: panic("bad redirect type"); break; } } static FDENV *fdenv_copy(FDENV *o) { FDENV *n; n = xget(sizeof(FDENV)); n->n = o->n; n->a = o->n; n->list = xget(n->n*sizeof(FDFD)); n->refs = 0; n->flags = 0; bcopy(o->list,n->list,n->n*sizeof(FDFD)); return(n); } static void setup_envs(PT *p) { PTRLIST *envstack; FDENV *env; static void envstack_push(void) { envstack = ptrlist_push(envstack,env); env = fdenv_copy(env); } static void envstack_pop(void) { if (env->refs == 0) fdenv_free(env); env = ptrlist_pop(&envstack); } static void pre(PT *p) { switch (p->type) { case PT_COMMAND: if (p->u.command.n_redir > 0) { envstack_push(); apply_redirects(p,env); } if (p->u.command.type == PT_CMD_SIMPLE) { env->refs ++; p->u.command.env = env; } break; case PT_BQ: envstack_push(); apply_redirects(p,env); break; default: break; } } static void post(PT *p) { switch (p->type) { case PT_COMMAND: if (p->u.command.n_redir > 0) envstack_pop(); break; case PT_BQ: envstack_pop(); break; default: break; } } envstack = 0; env = xget(sizeof(FDENV)); env->n = 3; env->a = 3; env->list = xget(3*sizeof(*env->list)); { int i; for (i=0;i<3;i++) { env->list[i].fd = i; env->list[i].type = FDFD_SHELL; env->list[i].u.shfd = i; } } env->refs = 0; env->flags = 0; pt_walk(p,&pre,&post); } static int pipename_match(PT *name, PT *against) { TOKEN *tn; TOKEN *ta; if (against == 0) return(0); pipename_check(name); pipename_check(against); tn = name->u.word.u.parts[0].u.text; ta = against->u.word.u.parts[0].u.text; if (tn->u.w.l != ta->u.w.l) return(0); return(!bcmp(tn->u.w.s,ta->u.w.s,tn->u.w.l)); } static int lookup_parallel(PT *r, PT *ll) { int i; PPIPE *pp; if (r->type != PT_REDIR) panic("lookup_parallel redir isn't"); if (r->u.redir.type != PT_REDIR_PIPE) panic("lookup_parallel pipe isn't"); pipename_check(r->u.redir.u.pipe.word); for (;ll;ll=ll->u.command.u.parallel.ll_thr) { if ((ll->type != PT_COMMAND) || (ll->u.command.type != PT_CMD_PARALLEL)) panic("lookup_parallel parallel isn't"); if (pipename_match(r->u.redir.u.pipe.word,ll->u.command.u.parallel.name)) { r->u.redir.u.pipe.parallel = ll; r->u.redir.u.pipe.pipeno = PIPENO_COPY; return(0); } for (i=ll->u.command.u.parallel.n_pipes-1;i>=0;i--) { pp = ll->u.command.u.parallel.pipes + i; if (pipename_match(r->u.redir.u.pipe.word,pp->name)) { r->u.redir.u.pipe.parallel = ll; r->u.redir.u.pipe.pipeno = i; #if 0 ll->u.command.u.parallel.pipes[i].refs[r->u.redir.u.pipe.which] ++; #endif return(0); } } } shp("No pipe named `%.*s' found.\n", r->u.redir.u.pipe.word->u.word.u.parts[0].u.text->u.w.l, r->u.redir.u.pipe.word->u.word.u.parts[0].u.text->u.w.s); return(1); } static int find_pipes(PT *p) { __label__ err; PT *ll; static void pre(PT *p) { switch (p->type) { case PT_COMMAND: if (p->u.command.type == PT_CMD_PARALLEL) { p->u.command.u.parallel.ll_thr = ll; ll = p; } break; case PT_REDIR: switch (p->u.redir.type) { case PT_REDIR_PIPE: if (lookup_parallel(p,ll)) goto err; break; default: break; } break; default: break; } } static void post(PT *p) { if ((p->type == PT_COMMAND) && (p->u.command.type == PT_CMD_PARALLEL)) { ll = ll->u.command.u.parallel.ll_thr; } } ll = 0; pt_walk(p,&pre,&post); return(0); err:; return(1); } static void expand_pipelines(PT *p) { static void fix(PT *p) { int n; PT **list; PT *s; int i; int j; if (state->debug) shp("expand_pipelines: %p\n",(void *)p); if (p->type != PT_PIPELINE) return; n = p->u.pts.n; list = p->u.pts.list; if (n < 1) panic("empty pipeline"); command_init(p); if (n == 1) { p->u.command.type = PT_CMD_PAREN; p->u.command.u.seq = list[0]; xput(list); return; } p->u.command.type = PT_CMD_PARALLEL; p->u.command.u.parallel.name = 0; p->u.command.u.parallel.n_pipes = n - 1; p->u.command.u.parallel.pipes = (n == 1) ? 0 : xget((n-1)*sizeof(PPIPE)); for (i=n-2;i>=0;i--) { p->u.command.u.parallel.pipes[i].name = 0; p->u.command.u.parallel.pipes[i].fds[0] = -1; p->u.command.u.parallel.pipes[i].fds[1] = -1; p->u.command.u.parallel.pipes[i].refs[0] = 0; p->u.command.u.parallel.pipes[i].refs[1] = 0; } p->u.command.u.parallel.n_seq = n; p->u.command.u.parallel.seqs = xget(n*sizeof(PT *)); for (i=n-1;i>=0;i--) { s = command_new(); s->u.command.type = PT_CMD_PAREN; s->u.command.n_redir = 2; if (i == 0) s->u.command.n_redir --; if (i == n-1) s->u.command.n_redir --; if (s->u.command.n_redir) { static void add_redir(int which, int pno) { PT *r; r = pt_new(); r->type = PT_REDIR; r->u.redir.type = PT_REDIR_PIPE; r->u.redir.r_thr = 0; r->u.redir.refcnt = 0; r->u.redir.usecnt = 0; r->u.redir.n = which; r->u.redir.u.pipe.word = 0; r->u.redir.u.pipe.parallel = p; r->u.redir.u.pipe.pipeno = pno; r->u.redir.u.pipe.which = which; s->u.command.redirs[j++] = r; } s->u.command.redirs = xget(s->u.command.n_redir*sizeof(PT *)); j = 0; if (i > 0) add_redir(0,i-1); if (i < n-1) add_redir(1,i); } else { s->u.command.redirs = 0; } s->u.command.u.seq = list[i]; p->u.command.u.parallel.seqs[i] = s; } xput(list); } pt_walk(p,0,&fix); } static void fix_fileand(PT *p) { static void fix(PT *p) { int i; PT *r; PT *r2; int of; PT *w; if (p->type != PT_COMMAND) return; #define C p->u.command for (i=C.n_redir-1;i>=0;i--) { r = C.redirs[i]; if (r->type != PT_REDIR) panic("redir isn't"); if (r->u.redir.type != PT_REDIR_FILEAND) continue; C.n_redir ++; C.redirs = xreget(C.redirs,C.n_redir*sizeof(PT *)); bcopy(C.redirs+i,C.redirs+i+1,(C.n_redir-i-1)*sizeof(PT *)); r2 = pt_new(); r2->type = PT_REDIR; r2->u.redir.type = PT_REDIR_DUP; r2->u.redir.r_thr = 0; r2->u.redir.refcnt = 0; r2->u.redir.usecnt = 0; r2->u.redir.n = 2; r2->u.redir.u.dup.m = 1; C.redirs[i] = r2; of = r->u.redir.u.fileand.oflags; w = r->u.redir.u.fileand.word; r->u.redir.type = PT_REDIR_FILE; r->u.redir.u.file.oflags = of; r->u.redir.u.file.word = w; r->u.redir.u.file.shfd = -1; } #undef C } pt_walk(p,0,&fix); } static void fix_copy_pipes(PT *p) { static void fix(PT *p) { FDFD *f; int lookfor; int i; FDENV *e; if (p->type != PT_COMMAND) return; e = p->u.command.env; if (e) { for (i=e->n-1;i>=0;i--) { f = &e->list[i]; if (f->type == FDFD_PIPE) { PT *pr; PT *pp; FDFD *pef; pr = f->u.redir; if (pr->type != PT_REDIR) panic("redir isn't"); switch (pr->u.redir.type) { default: panic("pipe redir isn't"); break; case PT_REDIR_PIPE: lookfor = pr->u.redir.u.pipe.which; break; } if (pr->u.redir.u.pipe.pipeno != PIPENO_COPY) continue; pp = pr->u.redir.u.pipe.parallel; if ( (pp->type != PT_COMMAND) || (pp->u.command.type != PT_CMD_PARALLEL) ) panic("pipe's parallel isn't"); pef = fd_in_env(pp->u.command.env,lookfor); if (pef) { FDFD t; t = *pef; t.fd = f->fd; *f = t; } else { f->type = FDFD_SHELL; f->u.shfd = lookfor; } } } } } pt_walk(p,&fix,0); } static void init_refs(PT *p) { static void check(PT *p) { int i; switch (p->type) { case PT_COMMAND: switch (p->u.command.type) { default: break; case PT_CMD_PARALLEL: #define P p->u.command.u.parallel for (i=P.n_pipes-1;i>=0;i--) { if (P.pipes[i].refs[0] || P.pipes[i].refs[1]) panic("nonzero refs"); } break; #undef P } if (p->u.command.env) p->u.command.env->flags &= ~FDEF_PROCESSED; break; case PT_REDIR: switch (p->u.redir.type) { case PT_REDIR_FILE: if (p->u.redir.u.file.refs) panic("nonzero refs"); break; default: break; } break; default: break; } } static void init(PT *p) { FDENV *e; int i; FDFD *f; PT *r; PT *pp; switch (p->type) { case PT_COMMAND: switch (p->u.command.type) { case PT_CMD_SIMPLE: e = p->u.command.env; if (e && !(e->flags & FDEF_PROCESSED)) { for (i=e->n-1;i>=0;i--) { f = &e->list[i]; switch (f->type) { default: break; case FDFD_FILE: r = f->u.redir; if ( (r->type != PT_REDIR) || (r->u.redir.type != PT_REDIR_FILE) ) { panic("file redir isn't"); } r->u.redir.u.file.refs ++; break; case FDFD_PIPE: r = f->u.redir; if (r->type != PT_REDIR) panic("redir isn't"); switch (r->u.redir.type) { case PT_REDIR_PIPE: break; default: panic("pipe redir isn't"); break; } pp = r->u.redir.u.pipe.parallel; if ( (pp->type != PT_COMMAND) || (pp->u.command.type != PT_CMD_PARALLEL) ) { panic("parallel command isn't"); } if (! PIPENO_SPECIAL(r->u.redir.u.pipe.pipeno)) { if ( (r->u.redir.u.pipe.pipeno < 0) || (r->u.redir.u.pipe.pipeno >= pp->u.command.u.parallel.n_pipes) ) { panic("impossible pipe number"); } pp->u.command.u.parallel.pipes[r->u.redir.u.pipe.pipeno].refs[r->u.redir.u.pipe.which] ++; } break; } } e->flags |= FDEF_PROCESSED; } break; default: break; } break; default: break; } } pt_walk(p,&check,0); pt_walk(p,&init,0); } static void do_dmoves(int ndmove, int (*dmove)[2]) { int i; int j; int k; int tmpfd; char done[ndmove]; int seq[ndmove]; int id; if (state->debug) { id = getpid(); shp("[%d: do_dmoves",id); for (i=0;i%d]",dmove[i][0],dmove[i][1]); shp("]\n"); } if (ndmove < 1) return; for <"nextfd"> (tmpfd=3;;tmpfd++) { for (i=0;i; } if (fcntl(tmpfd,F_GETFD,0) != -1) continue; break; } for (i=0;i (1) { for (k=0;;k++) { if (k >= ndmove) break <"more">; if (! done[k]) break; } if (dmove[k][0] == dmove[k][1]) { done[k] = 1; continue; } j = 0; while <"chain"> (1) { seq[j++] = k; done[k] = 1; for (i=0;;i++) { if (i >= ndmove) break <"chain">; if (!done[i] && (dmove[i][0] == dmove[k][1])) break; } k = i; } if (dmove[seq[j-1]][1] == dmove[seq[0]][0]) { if (state->debug) { shp("[%d: loop",id); for (i=0;i%d]",dmove[seq[i]][0],dmove[seq[i]][1]); shp("]\n"); } dup2(dmove[seq[j-1]][1],tmpfd); for (i=j-1;i>=0;i--) dup2(dmove[seq[i]][0],dmove[seq[i]][1]); dup2(tmpfd,dmove[seq[0]][0]); close(tmpfd); } else { if (state->debug) { shp("[%d: chain",id); for (i=0;i%d]",dmove[seq[i]][0],dmove[seq[i]][1]); shp("]\n"); } for (i=j-1;i>=0;i--) dup2(dmove[seq[i]][0],dmove[seq[i]][1]); } } for (i=0;instrs < 1) { return(ENOENT); } err = 0; for (i=0;instrs;i++) { const char *t; static char *tf = 0; char *e; e = p->strs[i]; if (!e[0] || ((e[0] == '.') && !e[1])) { t = av0; } else { xput(tf); tf = xaprintf("%s/%s",p->strs[i],av0); t = tf; } (*try)(t); switch (errno) { case ENOENT: case ENOEXEC: if (! err) { default: err = errno; } break; } } return(err); } static int start_simple(JOB *j, PT *p) { int i; PT *wl; PT *w; FDENV *e; int ndmove; int (*dmove)[2]; int argc; char **argv; if ( (p->type != PT_COMMAND) || (p->u.command.type != PT_CMD_SIMPLE) ) panic("simple command isn't"); wl = p->u.command.u.simple.wordlist; if (wl->type != PT_WORDLIST) panic("wordlist isn't"); if (! (wl->flags & PTF_DONE)) panic("undone wordlist"); if (! (wl->flags & PTF_SUCCESS)) panic("unsuccessful wordlist"); argc = wl->u.wl.n; for (i=0;iu.wl.list[i]; if (w->type != PT_WORD) panic("non-word in wordlist"); if (! (w->u.word.flags & PT_WF_TEXT)) panic("word without text"); } i = fork_for(j,p); if (i < 0) { setdone(p,0); return(1); } if (i == 0) { close(j->gopipe[1]); read(j->gopipe[0],&i,1); close(j->gopipe[0]); fflush(0); ndmove = 0; dmove = 0; e = p->u.command.env; for (i=e->n-1;i>=0;i--) { FDFD *f; int ffd; int tfd; PT *r; PT *pp; f = &e->list[i]; tfd = f->fd; switch (f->type) { default: panic("bad fdfd type"); break; case FDFD_CLOSE: ffd = -1; break; case FDFD_SHELL: ffd = f->u.shfd; break; case FDFD_FILE: r = f->u.redir; if ( (r->type != PT_REDIR) || (r->u.redir.type != PT_REDIR_FILE) ) panic("file redir isn't"); if (! (r->flags & PTF_DONE)) panic("undone file redir"); ffd = r->u.redir.u.file.shfd; break; case FDFD_PIPE: r = f->u.redir; if ( (r->type != PT_REDIR) || (r->u.redir.type != PT_REDIR_PIPE) ) panic("pipe redir isn't"); if (! (r->flags & PTF_DONE)) panic("undone pipe redir"); if (PIPENO_SPECIAL(r->u.redir.u.pipe.pipeno)) panic("special pipe"); pp = r->u.redir.u.pipe.parallel; if ( (pp->type != PT_COMMAND) || (pp->u.command.type != PT_CMD_PARALLEL) ) panic("parallel command isn't"); if ( (r->u.redir.u.pipe.pipeno < 0) || (r->u.redir.u.pipe.pipeno >= pp->u.command.u.parallel.n_pipes) ) panic("bad pipeno"); ffd = pp->u.command.u.parallel.pipes[r->u.redir.u.pipe.pipeno].fds[r->u.redir.u.pipe.which]; break; case FDFD_BQOUT: pp = f->u.bq; if (! (pp->u.bq.rtflags & PT_BQF_HAVE_W)) panic("no bq w"); ffd = pp->u.bq.pipe[1]; break; } if (ffd < 0) { close(tfd); } else { dmove = xreget(dmove,(ndmove+1)*sizeof(*dmove)); dmove[ndmove][0] = ffd; dmove[ndmove][1] = tfd; ndmove ++; } } cwdsync(); do_dmoves(ndmove,dmove); child_builtin(p->u.command.u.simple.wordlist); argc = wl->u.wl.n; argv = xget((argc+1)*sizeof(char *)); for (i=0;iu.wl.list[i]; argv[i] = w->u.word.u.text; } argv[argc] = 0; { static void execit(const char *argv0) { execve(argv0,argv,state->env->vars); } i = path_search(state->path,argv[0],execit); } fprintf(stderr,"proc %d failed to exec %s: %s\n",(int)getpid(),argv[0],strerror(i)); exit(1); } return(1); } static int start_it(JOB *j, PT *pt) { int i; int any; PT *p2; unsigned int f; if (pt == 0) return(0); if (pt->flags & (PTF_DONE|PTF_STARTED)) return(0); any = 0; switch (pt->type) { default: panic("start_it: bad pt type %d",pt->type); break; case PT_CMDLINE: if (pt->u.cmdline.bg) { shp("Backgrounded command-lines unimplemented\n"); setdone(pt,0); return(1); } any |= start_it(j,pt->u.cmdline.seq); any |= startcopy(pt,pt->u.cmdline.seq); break; case PT_COMMAND: for (i=pt->u.command.n_redir-1;i>=0;i--) { PT *r; r = pt->u.command.redirs[i]; if (r->type != PT_REDIR) panic("start_it redir isn't"); any |= start_it(j,r); } if (any) return(1); for (i=pt->u.command.n_redir-1;i>=0;i--) { if (! (pt->u.command.redirs[i]->flags & PTF_DONE)) return(0); } for (i=pt->u.command.n_redir-1;i>=0;i--) { if (! (pt->u.command.redirs[i]->flags & PTF_SUCCESS)) { setdone(pt,0); return(1); } } switch (pt->u.command.type) { default: panic("start_it bad command type %d",pt->u.command.type); break; case PT_CMD_SIMPLE: if (pt->u.command.u.simple.p) return(0); if (start_it(j,pt->u.command.u.simple.wordlist)) return(1); if (! (pt->u.command.u.simple.wordlist->flags & PTF_DONE)) return(0); if (! (pt->u.command.u.simple.wordlist->flags & PTF_SUCCESS)) { setdone(pt,0); return(1); } if (start_simple(j,pt)) { setstarted(pt); any = 1; } break; case PT_CMD_PAREN: any |= start_it(j,pt->u.command.u.seq); any |= startcopy(pt,pt->u.command.u.seq); break; case PT_CMD_SUBSHELL: { SHELLSTATE *t; t = state; state = pt->u.command.u.subshell.state; if (! state) state = shellstate_copy(t); any |= start_it(j,pt->u.command.u.subshell.seq); pt->u.command.u.subshell.state = state; state = t; any |= startcopy(pt,pt->u.command.u.subshell.seq); } break; case PT_CMD_PARALLEL: #define P pt->u.command.u.parallel if (P.n_seq < 1) panic("empty parallel"); any |= start_it(j,P.name); for (i=P.n_pipes-1;i>=0;i--) any |= start_it(j,P.pipes[i].name); if (any) return(1); if (P.name && !(P.name->flags & PTF_DONE)) return(any); for (i=P.n_pipes-1;i>=0;i--) if (P.pipes[i].name && !(P.pipes[i].name->flags & PTF_DONE)) return(any); if (P.name && !(P.name->flags & PTF_SUCCESS)) { setdone(pt,0); return(1); } for (i=P.n_pipes-1;i>=0;i--) { if (P.pipes[i].name && !(P.pipes[i].name->flags & PTF_SUCCESS)) { setdone(pt,0); return(1); } } for (i=P.n_pipes-1;i>=0;i--) { if ((P.pipes[i].fds[0] < 0) && (P.pipes[i].fds[1] < 0)) { if (pipe(&P.pipes[i].fds[0]) < 0) { shp("pipe: %s\n",strerror(errno)); setdone(pt,0); return(1); } shp("[parallel pipe [%d] %d %d]\n",i,P.pipes[i].fds[0],P.pipes[i].fds[1]); shfd_add(P.pipes[i].fds[0]); shfd_add(P.pipes[i].fds[1]); } else if ((P.pipes[i].fds[0] < 0) || (P.pipes[i].fds[1] < 0)) { panic("half-created pipe"); } } for (i=P.n_seq-1;i>=0;i--) any |= start_it(j,P.seqs[i]); return(any); #undef P break; case PT_CMD_NOWAIT: shp("%s %s unimplemented\n",tokname(TOK_ANDL),tokname(TOK_ANDR)); if (0) { case PT_CMD_BG: shp("%s %s unimplemented\n",tokname(TOK_ANDANDL),tokname(TOK_ANDR)); } setdone(pt,0); return(1); break; } break; case PT_WORDLIST: f = PTF_STARTED; for (i=pt->u.wl.n-1;i>=0;i--) { any |= start_it(j,pt->u.wl.list[i]); f &= pt->u.wl.list[i]->flags; } if (f & PTF_STARTED) setstarted(pt); break; case PT_SEQUENCE: case PT_OROR: case PT_ANDAND: if (pt->u.pts.n < 1) panic("run_it empty sequence"); f = PTF_STARTED; for (i=0;iu.pts.n;i++) { p2 = pt->u.pts.list[i]; if (p2->flags & PTF_DONE) { f &= p2->flags; continue; } any |= start_it(j,p2); return(any); } if (f & PTF_STARTED) setstarted(pt); return(0); break; case PT_PIPELINE: panic("starting pipeline"); break; case PT_REDIR: #define R pt->u.redir switch (R.type) { default: panic("start_it bad redir type %d",R.type); break; case PT_REDIR_FILE: #define F R.u.file if (F.word->type != PT_WORD) panic("file redir's word isn't"); if (start_it(j,F.word)) return(1); if (! (F.word->flags & PTF_DONE)) return(0); if (! (F.word->u.word.flags & PT_WF_TEXT)) panic("done word without text"); F.shfd = open(F.word->u.word.u.text,F.oflags,0666); if (F.shfd < 0) { shp("%s: %s\n",F.word->u.word.u.text,strerror(errno)); setdone(pt,0); } else { shfd_add(F.shfd); setdone(pt,1); } #undef F break; case PT_REDIR_FILEAND: panic("starting fileand redir"); break; case PT_REDIR_DUP: case PT_REDIR_PIPE: setdone(pt,1); return(1); break; } #undef R break; case PT_WORD: any = 0; f = PTF_STARTED; for (i=pt->u.word.len-1;i>=0;i--) { switch (pt->u.word.u.parts[i].type) { case WPT_BQ: any |= start_it(j,pt->u.word.u.parts[i].u.bq); f &= pt->u.word.u.parts[i].u.bq->flags; break; default: break; } } if (any) return(1); for (i=pt->u.word.len-1;i>=0;i--) { switch (pt->u.word.u.parts[i].type) { case WPT_BQ: if (! (pt->u.word.u.parts[i].u.bq->flags & PTF_DONE)) return(0); break; default: break; } } for (i=pt->u.word.len-1;i>=0;i--) { switch (pt->u.word.u.parts[i].type) { case WPT_BQ: if (! (pt->u.word.u.parts[i].u.bq->flags & PTF_SUCCESS)) { setdone(pt,0); return(1); } break; default: break; } } break; case PT_BQ: if (! (pt->u.bq.rtflags & PT_BQF_LISTED)) { if (pt->u.bq.rtflags & (PT_BQF_HAVE_R|PT_BQF_HAVE_W)) panic("half-done backquote"); pipe(&pt->u.bq.pipe[0]); shp("[bq pipe %d %d]\n",pt->u.bq.pipe[0],pt->u.bq.pipe[1]); shfd_add(pt->u.bq.pipe[0]); shfd_add(pt->u.bq.pipe[1]); pt->u.bq.rtflags |= PT_BQF_HAVE_R | PT_BQF_HAVE_W; pt->u.bq.buf = 0; pt->u.bq.len = 0; bq_list(pt); } any |= start_it(j,pt->u.bq.seq); any |= startcopy(pt,pt->u.bq.seq); break; } return(any); } static void give_tty(JOB *j) { if (! (j->flags & JF_HAVEPG)) panic("giving tty to pgless job"); ioctl(0,TIOCSPGRP,&j->pg); } static void job_go(JOB *j) { if (j->flags & JF_HAVEPIPE) { shfd_close(j->gopipe[0]); shfd_close(j->gopipe[1]); j->flags &= ~JF_HAVEPIPE; } } static void bq_read(PT *p) { int nr; char rbuf[8192]; if (p->type != PT_BQ) panic("reading from non-bq"); if (! (p->u.bq.rtflags & PT_BQF_HAVE_R)) panic("bq has no rpipe"); nr = read(p->u.bq.pipe[0],&rbuf[0],sizeof(rbuf)); if (nr < 0) { if (errno == EINTR) return; shp("backquote pipe read; %s\n",strerror(errno)); nr = 0; } if (nr == 0) { shfd_close(p->u.bq.pipe[0]); p->u.bq.rtflags = (p->u.bq.rtflags & ~PT_BQF_HAVE_R) | PT_BQF_GOTEOF; shp("[bq %p read EOF]\n",(void *)p); } else { p->u.bq.buf = xreget(p->u.bq.buf,p->u.bq.len+nr); bcopy(&rbuf[0],p->u.bq.buf+p->u.bq.len,nr); p->u.bq.len += nr; shp("[bq %p read %d]\n",(void *)p,nr); } } static void await(void) { static struct pollfd *pfd = 0; static int a = 0; int n; int rpx; PT *bq; int rv; static int addfd(int fd) { if (n >= a) pfd = xreget(pfd,(a=n+1)*sizeof(struct pollfd)); pfd[n].fd = fd; pfd[n].events = POLLIN | POLLRDNORM; return(n++); } n = 0; rpx = addfd(sigchld_rpipe); for (bq=bqhead;bq;bq=bq->u.bq.flink) { if (bq->type != PT_BQ) panic("non-bq on list"); bq->u.bq.px = addfd(bq->u.bq.pipe[0]); } rv = poll(pfd,n,INFTIM); if (rv < 0) { if (errno == EINTR) return; shp("poll: %s\n",strerror(errno)); return; } if (pfd[rpx].revents) { reset_sigchld_pipe(); } for (bq=bqhead;bq;bq=bq->u.bq.flink) { if (bq->type != PT_BQ) panic("non-bq on list"); if (bq->u.bq.px < 0) continue; if (pfd[bq->u.bq.px].revents) bq_read(bq); bq->u.bq.px = -1; } } int main(int, char **, char **); int main(int ac, char **av, char **env) { ac=ac;av=av; setup_files(); setup_maps(); setup_history(&histroot,&histsize); setup_history(&killring,&killsize); shellstate_init(env); setpgid(0,0); mypg = getpid(); job_link_init(); setup_signals(); while (1) { taketty(); cwdsync(); if (! get_line("... ")) exit(0); history_save(&histroot,iline,ilen); shp(">>> %.*s\n",ilen,iline); tokenize_line(); shp("Tokens:"); { int i; for (i=0;itype)); } shp("\n"); if (ntokens > 0) { if (! cmdline_builtin()) { JOB *j; attoken = 0; cmdline = parse_command_line(); if (cmdline == 0) continue; pt_dump(cmdline,0,"initial"); pt_walk( cmdline, ({ static void foo(PT *p) { p->flags &= ~(PTF_DONE|PTF_SUCCESS); } &foo; }), 0 ); if (! find_pipes(cmdline)) { pt_dump(cmdline,0,"point 1"); expand_pipelines(cmdline); pt_dump(cmdline,0,"point 2"); fix_fileand(cmdline); pt_dump(cmdline,0,"point 3"); setup_envs(cmdline); pt_dump(cmdline,0,"point 4"); fix_copy_pipes(cmdline); pt_dump(cmdline,0,"point 5"); init_refs(cmdline); pt_dump(cmdline,0,"point 6"); j = new_job(); while (1) { check_deaths(); check_done(cmdline); pt_dump(cmdline,0,"after check_done"); if (cmdline->flags & PTF_DONE) break; if (start_it(j,cmdline)) continue; pt_dump(cmdline,0,"giving tty"); give_tty(j); job_go(j); pt_dump(cmdline,0,"before await"); await(); } /* take_tty(); */ shp("[done]\n"); free_job(j); } pt_free(cmdline); } } free_line_tokens(); xput(iline); } }