#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_NONE = 1, FDFD_CLOSE, FDFD_SHELL, FDFD_FILE, FDFD_PIPE, FDFD_BQOUT, } FDFD_T; 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 { ET_EXIT = 1, ET_SIG, } EXITSTAT_T; 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]; unsigned int flags; #define PPF_DONE 0x00000001 } ; struct ptrlist { PTRLIST *link; void *p; } ; typedef enum { WPT_TEXT = 1, WPT_BQ, } WORDPART_T; struct wordpart { WORDPART_T type; union { TOKEN *text; PT *bq; } u; } ; typedef enum { PT_NONE = 1, PT_CMDLINE, PT_COMMAND, PT_WORDLIST, PT_SEQUENCE, PT_OROR, PT_ANDAND, PT_PIPELINE, PT_REDIR, PT_WORD, PT_BQ, PT_LOOPCTL, } PT_T; typedef enum { 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, PT_CMD_LOOP, } PT_COMMAND_T; typedef enum { PT_REDIR_NONE = 1, PT_REDIR_FILE, PT_REDIR_FILEAND, PT_REDIR_DUP, PT_REDIR_PIPE, } PT_REDIR_T; typedef enum { PT_LOOP_WHILE = 1, PT_LOOP_UNLESS, } PT_LOOPCTL_T; 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, LOOP */ 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; struct { PT_LOOPCTL_T type; } loopctl; } u; } ; typedef enum { 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, /* ?; */ TOK_LOOP, /* (@ */ TOK_WHILE, /* @. */ TOK_UNLESS, /* @!. */ } TOKEN_T; 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 { JKS_RUN = 1, JKS_STOP, JKS_DEAD, } JOBKID_S; 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 *upcase; unsigned char *dncase; void (*disp)(unsigned char, void (*)(int)); } ; 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 { KT_UNBOUND = 1, KT_LEAF, KT_MAP, } KEY_T; 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_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 void charset_iso_8859_1_disp(unsigned char, void (*)(int)); static CHARSET charset_iso_8859_1 = { &charset_iso_8859_1_names[0], &charset_iso_8859_1_flags[0], &charset_iso_8859_1_upcase[0], &charset_iso_8859_1_dncase[0], &charset_iso_8859_1_disp }; 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 charset_iso_8859_1_disp(unsigned char ch, void (*cb)(int)) { if (ch < 32) { (*cb)('^'); (*cb)(ch+64); } else if (ch < 127) { (*cb)(ch); } else if (ch < 128) { (*cb)('^'); (*cb)('?'); } else if (ch < 160) { (*cb)('\\'); (*cb)('2'); (*cb)(((ch>>3)&7)+'0'); (*cb)((ch&7)+'0'); } else { (*cb)(ch); } } 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 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;icharset->disp)(ibuf[i],&wchar); } 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_UNLESS }, { "(``", 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_LOOP }, { "@.", TOK_WHILE }, { "&", 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 '|': case '@' #define SPECIAL_SOLO \ '&': case '(': case ')': case ';': \ case '<': case '>': case '|' #define SPECIAL_NONSOLO \ '`': case '?': 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; case TOK_LOOP: return("(@"); break; case TOK_WHILE: return("@."); break; case TOK_UNLESS: 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: case TOK_LOOP: case TOK_WHILE: case TOK_UNLESS: 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 init_ppipe(PPIPE *p) { p->name = 0; p->fds[0] = -1; p->fds[1] = -1; p->refs[0] = 0; p->refs[1] = 0; p->flags = 0; } 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); init_ppipe(&pt->u.command.u.parallel.pipes[i]); pt->u.command.u.parallel.pipes[i].name = w; 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 void parse_command_loop(PT *pt) { __label__ failret; PT **seqv; int seqn; PT *sub; 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 failret; } void add_sub(PT *p) { seqv = xreget(seqv,(seqn+1)*sizeof(PT *)); seqv[seqn++] = p; } seqv = 0; seqn = 0; while <"loop"> (1) { tok_skip(); sub = parse_sequence(); if (sub == 0) fail("Missing sequence"); add_sub(sub); switch (tok_next(0)->type) { case TOK_WHILE: sub = pt_new(); sub->type = PT_LOOPCTL; sub->u.loopctl.type = PT_LOOP_WHILE; break; case TOK_UNLESS: sub = pt_new(); sub->type = PT_LOOPCTL; sub->u.loopctl.type = PT_LOOP_UNLESS; break; case TOK_RPAR: break <"loop">; default: fail("Improperly closed %s",tokname(TOK_LOOP)); break; } } tok_skip(); sub = pt_new(); sub->type = PT_SEQUENCE; sub->u.pts.n = seqn; sub->u.pts.list = seqv; pt->u.command.type = PT_CMD_LOOP; pt->u.command.u.seq = sub; return; failret:; while (seqn > 0) pt_free(seqv[--seqn]); xput(seqv); } 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; case TOK_LOOP: parse_command_loop(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; case PT_CMD_COND: shp("COND\n"); break; case PT_CMD_LOOP: shp("LOOP\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: pt_dump(pt->u.command.u.cond.test,indent+1,"test"); pt_dump(pt->u.command.u.cond.ift,indent+1,"then"); pt_dump(pt->u.command.u.cond.iff,indent+1,"else"); break; case PT_CMD_LOOP: shp("(PT_CMD_LOOP 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 & ~PT_WF_TEXT) == 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_QUOTE) 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; case PT_LOOPCTL: shp("LOOPCTL: "); switch (pt->u.loopctl.type) { default: shp("type %d",(int)pt->u.loopctl.type); break; case PT_LOOP_WHILE: shp("WHILE"); break; case PT_LOOP_UNLESS: shp("UNLESS"); break; } shp("\n"); 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); } shfd_add(j->gopipe[0]); shfd_add(j->gopipe[1]); shp("[job %p gopipe %d %d]\n",(void *)j,j->gopipe[0],j->gopipe[1]); j->flags |= JF_HAVEPIPE; } 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: case PT_CMD_LOOP: 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; case PT_CMD_COND: walk(p->u.command.u.cond.test); walk(p->u.command.u.cond.ift); walk(p->u.command.u.cond.iff); 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. This definition is carefully designed to DTRT for NOWAIT and * BG. * * Return true iff we actually change p's flags. */ 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. * * This code returns true if it did anything. */ 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; case PT_CMD_COND: any |= check_done(C.u.cond.test); if (C.u.cond.test->flags & PTF_DONE) { p2 = (C.u.cond.test->flags & PTF_SUCCESS) ? C.u.cond.ift : C.u.cond.iff; any |= check_done(p2); any |= donecopy(p,p2); } 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--) init_ppipe(&p->u.command.u.parallel.pipes[i]); 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); } /* * start_it returns true if it made any progress. (If something is * fatally wrong, call setdone(,0) and return true.) */ static int start_it_(JOB *, PT *, int); static int start_it_indent; static int start_it(JOB *j, PT *pt) { int rv; start_it_indent = 0; rv = start_it_(j,pt,start_it_indent); if (start_it_indent != 0) panic("mis-nested start_it"); return(rv); } static int start_it__(JOB *j, PT *pt) { int rv; start_it_indent += 2; rv = start_it_(j,pt,start_it_indent); start_it_indent -= 2; return(rv); } #define start_it start_it__ static int start_it_(JOB *j, PT *pt, int indent) { int i; int any; PT *p2; unsigned int f; if (pt == 0) return(0); shp("%*sstart_it %p %p\n",indent,"",(void *)j,(void *)pt); if (pt->flags & PTF_DONE) { shp("%*sstart_it %p already done\n",indent,"",(void *)pt); return(0); } if (pt->flags & PTF_STARTED) { shp("%*sstart_it %p already started\n",indent,"",(void *)pt); return(0); } any = 0; switch (pt->type) { default: panic("start_it: bad pt type %d",pt->type); break; case PT_CMDLINE: shp("%*sstart_it %p CMDLINE\n",indent,"",(void *)pt); if (pt->u.cmdline.bg) { shp("Backgrounded command-lines unimplemented\n"); setdone(pt,0); return(1); } shp("%*sstart_it %p CMDLINE start_it %p\n",indent,"",(void *)pt,(void *)pt->u.cmdline.seq); any |= start_it(j,pt->u.cmdline.seq); shp("%*sstart_it %p CMDLINE startcopy %p\n",indent,"",(void *)pt,(void *)pt->u.cmdline.seq); any |= startcopy(pt,pt->u.cmdline.seq); shp("%*sstart_it %p CMDLINE exiting %d\n",indent,"",(void *)pt,any); break; case PT_COMMAND: shp("%*sstart_it %p COMMAND\n",indent,"",(void *)pt); 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"); shp("%*sstart_it %p start_it redir %d %p\n",indent,"",(void *)pt,i,(void *)r); any |= start_it(j,r); } for (i=pt->u.command.n_redir-1;i>=0;i--) { if (! (pt->u.command.redirs[i]->flags & PTF_DONE)) { shp("%*sstart_it %p redir %d not done\n",indent,"",(void *)pt,i); return(any); } } for (i=pt->u.command.n_redir-1;i>=0;i--) { if (! (pt->u.command.redirs[i]->flags & PTF_SUCCESS)) { shp("%*sstart_it %p redir %d failed\n",indent,"",(void *)pt,i); 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: shp("%*sstart_it %p CMD_SIMPLE\n",indent,"",(void *)pt); if (pt->u.command.u.simple.p) { shp("%*sstart_it %p CMD_SIMPLE already have kid %p\n",indent,"",(void *)pt,(void *)pt->u.command.u.simple.p); return(0); } shp("%*sstart_it %p CMD_SIMPLE starting wordlist %p\n",indent,"",(void *)pt,(void *)pt->u.command.u.simple.wordlist); any |= start_it(j,pt->u.command.u.simple.wordlist); if (! (pt->u.command.u.simple.wordlist->flags & PTF_DONE)) { shp("%*sstart_it %p CMD_SIMPLE wordlist not done\n",indent,"",(void *)pt); return(any); } if (! (pt->u.command.u.simple.wordlist->flags & PTF_SUCCESS)) { shp("%*sstart_it %p CMD_SIMPLE wordlist failed\n",indent,"",(void *)pt); setdone(pt,0); return(1); } shp("%*sstart_it %p CMD_SIMPLE calling start_simple\n",indent,"",(void *)pt); if (start_simple(j,pt)) { shp("%*sstart_it %p CMD_SIMPLE start_simple worked, setting started\n",indent,"",(void *)pt); setstarted(pt); any = 1; } shp("%*sstart_it %p CMD_SIMPLE exiting %d\n",indent,"",(void *)pt,any); break; case PT_CMD_PAREN: shp("%*sstart_it %p CMD_PAREN\n",indent,"",(void *)pt); shp("%*sstart_it %p CMD_PAREN start_it %p\n",indent,"",(void *)pt,(void *)pt->u.command.u.seq); any |= start_it(j,pt->u.command.u.seq); shp("%*sstart_it %p CMD_PAREN startcopy %p\n",indent,"",(void *)pt,(void *)pt->u.command.u.seq); any |= startcopy(pt,pt->u.command.u.seq); shp("%*sstart_it %p CMD_PAREN exiting %d\n",indent,"",(void *)pt,any); break; case PT_CMD_SUBSHELL: { SHELLSTATE *t; shp("%*sstart_it %p CMD_SUBSHELL\n",indent,"",(void *)pt); t = state; state = pt->u.command.u.subshell.state; if (! state) state = shellstate_copy(t); shp("%*sstart_it %p CMD_SUBSHELL start_it %p\n",indent,"",(void *)pt,(void *)pt->u.command.u.subshell.seq); any |= start_it(j,pt->u.command.u.subshell.seq); pt->u.command.u.subshell.state = state; state = t; shp("%*sstart_it %p CMD_SUBSHELL startcopy %p\n",indent,"",(void *)pt,(void *)pt->u.command.u.subshell.seq); any |= startcopy(pt,pt->u.command.u.subshell.seq); shp("%*sstart_it %p CMD_SUBSHELL exiting %d\n",indent,"",(void *)pt,any); } break; case PT_CMD_PARALLEL: #define P pt->u.command.u.parallel shp("%*sstart_it %p CMD_PARALLEL\n",indent,"",(void *)pt); if (P.n_seq < 1) panic("empty parallel"); shp("%*sstart_it %p CMD_PARALLEL start_it %p\n",indent,"",(void *)pt,(void *)P.name); any |= start_it(j,P.name); for (i=P.n_pipes-1;i>=0;i--) { shp("%*sstart_it %p CMD_PARALLEL start_it %p\n",indent,"",(void *)pt,(void *)P.pipes[i].name); any |= start_it(j,P.pipes[i].name); } if (P.name && !(P.name->flags & PTF_DONE)) { shp("%*sstart_it %p CMD_PARALLEL own name not done\n",indent,"",(void *)pt); return(any); } for (i=P.n_pipes-1;i>=0;i--) if (P.pipes[i].name && !(P.pipes[i].name->flags & PTF_DONE)) { shp("%*sstart_it %p CMD_PARALLEL pipe %d name not done\n",indent,"",(void *)pt,i); return(any); } if (P.name && !(P.name->flags & PTF_SUCCESS)) { shp("%*sstart_it %p CMD_PARALLEL own name failed\n",indent,"",(void *)pt); 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)) { shp("%*sstart_it %p CMD_PARALLEL pipe %d name failed\n",indent,"",(void *)pt,i); setdone(pt,0); return(1); } } for (i=P.n_pipes-1;i>=0;i--) { if (P.pipes[i].flags & PPF_DONE) continue; 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]); P.pipes[i].flags |= PPF_DONE; any = 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--) { shp("%*sstart_it %p CMD_PARALLEL start_it %p\n",indent,"",(void *)pt,(void *)P.seqs[i]); any |= start_it(j,P.seqs[i]); } f = PTF_STARTED; for (i=P.n_seq-1;i>=0;i--) f &= P.seqs[i]->flags; if (f) { setstarted(pt); any = 1; } shp("%*sstart_it %p CMD_PARALLEL exiting %d\n",indent,"",(void *)pt,any); #undef P break; case PT_CMD_COND: #define C pt->u.command.u.cond shp("%*sstart_it %p CMD_COND\n",indent,"",(void *)pt); if (C.test->flags & PTF_DONE) { p2 = (C.test->flags & PTF_SUCCESS) ? C.ift : C.iff; shp("%*sstart_it %p CMD_COND starting %s arm %p\n",indent,"",(void *)pt,(C.test->flags&PTF_SUCCESS)?"true":"false",(void *)p2); any |= start_it(j,p2); } else if (! (C.test->flags & PTF_STARTED)) { shp("%*sstart_it %p CMD_COND starting test %p\n",indent,"",(void *)pt,(void *)C.test); any |= start_it(j,C.test); } shp("%*sstart_it %p CMD_COND exiting %d\n",indent,"",(void *)pt,any); #undef C break; case PT_CMD_NOWAIT: shp("%*sstart_it %p CMD_NOWAIT\n",indent,"",(void *)pt); shp("%s %s unimplemented\n",tokname(TOK_ANDL),tokname(TOK_ANDR)); if (0) { case PT_CMD_BG: shp("%*sstart_it %p CMD_BG\n",indent,"",(void *)pt); shp("%s %s unimplemented\n",tokname(TOK_ANDANDL),tokname(TOK_ANDR)); } setdone(pt,0); shp("%*sstart_it %p CMD_NOWAIT|CMD_BG exiting %d\n",indent,"",(void *)pt,1); return(1); break; } break; case PT_WORDLIST: shp("%*sstart_it %p WORDLIST\n",indent,"",(void *)pt); f = PTF_STARTED | PTF_DONE; for (i=pt->u.wl.n-1;i>=0;i--) { shp("%*sstart_it %p WORDLIST start_it %p\n",indent,"",(void *)pt,(void *)pt->u.wl.list[i]); any |= start_it(j,pt->u.wl.list[i]); f &= pt->u.wl.list[i]->flags; } /* don't setdone() here; must pass thorugh check_done */ if (f) { setstarted(pt); any = 1; } shp("%*sstart_it %p WORDLIST exiting %d\n",indent,"",(void *)pt,any); break; case PT_SEQUENCE: case PT_OROR: case PT_ANDAND: shp("%*sstart_it %p SEQ-like\n",indent,"",(void *)pt); 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; } shp("%*sstart_it %p SEQ-like start_it %d %p\n",indent,"",(void *)pt,i,(void *)p2); any |= start_it(j,p2); shp("%*sstart_it %p SEQ-like %d returning %d\n",indent,"",(void *)pt,i,any); return(any); } if (f & PTF_STARTED) setstarted(pt); shp("%*sstart_it %p SEQ-like exiting %d\n",indent,"",(void *)pt,0); return(0); break; case PT_PIPELINE: panic("starting pipeline"); break; case PT_REDIR: #define R pt->u.redir shp("%*sstart_it %p REDIR\n",indent,"",(void *)pt); 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"); shp("%*sstart_it %p REDIR start_it %p\n",indent,"",(void *)pt,(void *)F.word); any |= start_it(j,F.word); if (! (F.word->flags & PTF_DONE)) { shp("%*sstart_it %p REDIR word not done\n",indent,"",(void *)pt); return(any); } 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); } any = 1; #undef F shp("%*sstart_it %p REDIR exiting %d\n",indent,"",(void *)pt,any); break; case PT_REDIR_FILEAND: panic("starting fileand redir"); break; case PT_REDIR_DUP: case PT_REDIR_PIPE: shp("%*sstart_it %p DUP|PIPE trivial\n",indent,"",(void *)pt); setdone(pt,1); return(0); break; } #undef R break; case PT_WORD: shp("%*sstart_it %p WORD\n",indent,"",(void *)pt); 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: shp("%*sstart_it %p WORD start_it BQ part %d %p\n",indent,"",(void *)pt,i,(void *)pt->u.word.u.parts[i].u.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; } } 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)) { shp("%*sstart_it %p WORD BQ part %d not done\n",indent,"",(void *)pt,i); return(any); } 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)) { shp("%*sstart_it %p WORD BQ part %d failed\n",indent,"",(void *)pt,i); setdone(pt,0); return(1); } break; default: break; } } setdone(pt,1); any = 1; shp("%*sstart_it %p WORD exiting %d\n",indent,"",(void *)pt,any); break; case PT_BQ: shp("%*sstart_it %p BQ\n",indent,"",(void *)pt); 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); } shp("%*sstart_it %p BQ start_it %p\n",indent,"",(void *)pt,(void *)pt->u.bq.seq); any |= start_it(j,pt->u.bq.seq); shp("%*sstart_it %p BQ startcopy %p\n",indent,"",(void *)pt,(void *)pt->u.bq.seq); any |= startcopy(pt,pt->u.bq.seq); shp("%*sstart_it %p BQ exiting %d\n",indent,"",(void *)pt,any); break; } return(any); } #undef start_it 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); } }