/* This file is in the public domain. */ #include #include #include #include #include #include #include #include #include #include #include "ecl-glue.h" #include "ecl-path.h" #include "ecl-malloc.h" #define TTYRINGSIZE 8192 #define GETATTR(tio) tcgetattr(fd,tio) #ifdef TCSASOFT #define SETATTR_ARG (TCSADRAIN|TCSASOFT) #else #define SETATTR_ARG (TCSADRAIN) #endif #define SETATTR(tio) tcsetattr(fd,SETATTR_ARG,tio) /* Dammit. , via , preempts CTRL()! */ #define CTRL_(x) ((x)^64) #define ARRAYSIZE(array) (sizeof(array)/sizeof(array[0])) #define HL(x) (((x)*2)+1) #define HR(x) (((x)+1)*2) #define HU(x) (((x)-1)/2) typedef enum { FXN_UNSET = 1, FXN_PRIMITIVE, FXN_MACRO } FXNTYPE; typedef enum { MAC_F = 1, MAC_K } MACTYPE; typedef enum { BINDING_UNBOUND = 1, BINDING_FXN, BINDING_KEYMAP } BINDTYPE; typedef enum { BFS_BADKEYNAME = 1, BFS_MISSINGKEY, BFS_FOUND } BFSTAT; typedef enum { KS_BADKEYNAME = 1, KS_MISSINGKEY, KS_OK } KSSTAT; typedef enum { MODE_NORMAL = 1, MODE_RETURN } OPMODE; typedef enum { CK_UNSPECIAL = 1, CK_HISTORY, CK_DELETE, CK_YANK, CK_PATH, CK_TCSHHIST } CMDKIND; typedef enum { QRS_STR_A = 1, QRS_STR_B, QRS_QUERY, QRS_SHOW } QRSTAGE; typedef enum { HD_NEW_TO_OLD = 1, HD_OLD_TO_NEW } HISTDIR; typedef unsigned int NAMEHASH; typedef struct primitive PRIMITIVE; typedef struct macro MACRO; typedef struct fxn FXN; typedef struct binding BINDING; typedef struct keymap KEYMAP; typedef struct priminit PRIMINIT; typedef struct bindinit BINDINIT; typedef struct ttystate TTYSTATE; typedef struct bindfind BINDFIND; typedef struct linestate LINESTATE; typedef struct sl SL; typedef struct histstate HISTSTATE; typedef struct charprop CHARPROP; typedef struct kseq KSEQ; typedef struct statestack STATESTACK; typedef struct qrstate QRSTATE; typedef struct ssops SSOPS; typedef struct macstack MACSTACK; #define NMAPKEYS 256 struct charprop { unsigned char flags; #define CPF_WORDCHAR 0x01 #define CPF_DIGIT 0x02 #define CPF_SPACE 0x04 unsigned char lcmap; unsigned char ucmap; } ; struct sl { unsigned char *s; int l; } ; struct kseq { KSSTAT status; int keyoff; unsigned char *seq; int seqlen; } ; struct bindfind { BFSTAT status; int keyoff; struct { KEYMAP *map; unsigned char key; } found; } ; struct ttystate { struct termios tio; } ; struct primitive { FXN *fxn; void (*impl)(void); int flags; #define PRIMF_WANTSCHAR 0x00000001 } ; /* * A MACRO's refcnt counts 1 for each MACSTACK that points to it plus 1 * for each FXN that points to it (never more than one FXN). */ struct macro { FXN *fxn; MACTYPE type; int refcnt; int disabled; int size; unsigned char *keys; FXN **fxns; } ; struct macstack { MACSTACK *link; MACRO *mac; int at; } ; /* * usecnt counts "uses", which are defined as bindings or macro items * that point to the FXN. A single macro using the same FXN multiple * times increments the FXN's usecount once for each of those times. * A macro's fxn pointer does not count as a use. */ struct fxn { NAMEHASH namehash; unsigned char *name; FXN *l; FXN *r; FXNTYPE type; int usecnt; union { PRIMITIVE *primitive; MACRO *macro; } ; } ; struct binding { BINDTYPE type; union { FXN *fxn; KEYMAP *keymap; } ; } ; /* * A KEYMAP's refcnt counts 1 if the keymap is km_top, 1 if the keymap * is km_cur, 1 for each STATESTACK whose topkm it is, 1 for each * binding that's bound to it, and 1 if the KEYMAP is a well-known * keymap (if it has a static variable dedicated to holding it). */ struct keymap { int refcnt; unsigned char *name; FXN *fxns; KEYMAP *freelink; KEYMAP *alllink; BINDING keys[NMAPKEYS]; } ; struct priminit { const char *name; void (*impl)(void); int wantschar; } ; struct bindinit { const char *seq; const char *fxn; } ; struct linestate { int l; int a; unsigned char *b; int c; int m; } ; struct histstate { SL *lines; int size; int h; int t; int n; int at; int insat; int insn; unsigned char *ss; int ssa; int ssl; int ro; int keepempty; } ; #define HIST_NEXT(hs,v) (((v)==(hs)->size-1)?0:((v)+1)) #define HIST_PREV(hs,v) (((v)?:(hs)->size)-1) #define HIST_SUB(hs,a,b) (((a)+(hs)->size-(b))%(hs)->size) #define HIST_ADD(hs,a,b) (((a)+(b))%(hs)->size) struct ssops { void *(*init)(void); void (*finish)(void *); void (*abort)(void *); void (*reprompt)(void *); } ; struct statestack { STATESTACK *link; LINESTATE line; OPMODE mode; int hist_ro; int hist_at; KEYMAP *topkm; const SSOPS *ops; void *cookie; } ; struct qrstate { QRSTAGE stage; SL a; SL b; KEYMAP *prevtop; int fromc; int nfound; int showmark; } ; static int initted = 0; static int left_a; static int left_l; static int left_p; static char *left_b; static LINESTATE desl; static LINESTATE ddl; static LINESTATE cdl; static int full_redraw; static TTYSTATE o_ts; static TTYSTATE n_ts; static int ttyerrs; static int fd; static KEYMAP *km_top; static KEYMAP *km_cur; static KEYMAP *km_free; static KEYMAP *km_all; static KEYMAP *km_root; static KEYMAP *km_qr; static KEYMAP *km_lnext; static unsigned char last_key_struck; static OPMODE mode; static CHARPROP charprop[256]; static int cpvalid; static HISTSTATE hist; #define HA_BEFORE_OLDEST (-1) #define HA_AFTER_NEWEST (-2) #define HA_TCSH_GAP (-3) static CMDKIND thisck; static CMDKIND lastck; static int tty_pushed; static unsigned char tty_push; static int last_del_n; static int last_del_at; static HISTSTATE killring; static int pathlen; static STATESTACK *statestack; static QRSTATE *qr_cur; static HISTDIR histsearch_dir; static SL histsearch_string; static int tty_state_saved = 0; static int tty_state_locked = 0; static MACSTACK *macstack; static char *mark_display = 0; static int mark_display_len; static unsigned char tty_ring[TTYRINGSIZE]; static int tty_ring_h = 0; static int tty_ring_t = 0; static const unsigned char *keynames[] = { "\000^\100", "\000\\000", "\000o000", "\001^\140", "\000NUL", "\000nul", "\001^\101", "\001\\001", "\001o001", "\001^\141", "\001SOH", "\001soh", "\002^\102", "\002\\002", "\002o002", "\002^\142", "\002STX", "\002stx", "\003^\103", "\003\\003", "\003o003", "\003^\143", "\003ETX", "\003etx", "\004^\104", "\004\\004", "\004o004", "\004^\144", "\004EOT", "\004eot", "\005^\105", "\005\\005", "\005o005", "\005^\145", "\005ENQ", "\005enq", "\006^\106", "\006\\006", "\006o006", "\006^\146", "\006ACK", "\006ack", "\007^\107", "\007\\007", "\007o007", "\007^\147", "\007BEL", "\007bel", "\010^\110", "\010\\010", "\010o010", "\010^\150", "\010BS", "\010bs", "\011^\111", "\011\\011", "\011o011", "\011^\151", "\011HT", "\011ht", "\012^\112", "\012\\012", "\012o012", "\012^\152", "\012NL", "\012nl", "\013^\113", "\013\\013", "\013o013", "\013^\153", "\013VT", "\013vt", "\014^\114", "\014\\014", "\014o014", "\014^\154", "\014NP", "\014np", "\015^\115", "\015\\015", "\015o015", "\015^\155", "\015CR", "\015cr", "\016^\116", "\016\\016", "\016o016", "\016^\156", "\016SO", "\016so", "\017^\117", "\017\\017", "\017o017", "\017^\157", "\017SI", "\017si", "\020^\120", "\020\\020", "\020o020", "\020^\160", "\020DLE", "\020dle", "\021^\121", "\021\\021", "\021o021", "\021^\161", "\021DC1", "\021dc1", "\022^\122", "\022\\022", "\022o022", "\022^\162", "\022DC2", "\022dc2", "\023^\123", "\023\\023", "\023o023", "\023^\163", "\023DC3", "\023dc3", "\024^\124", "\024\\024", "\024o024", "\024^\164", "\024DC4", "\024dc4", "\025^\125", "\025\\025", "\025o025", "\025^\165", "\025NAK", "\025nak", "\026^\126", "\026\\026", "\026o026", "\026^\166", "\026SYN", "\026syn", "\027^\127", "\027\\027", "\027o027", "\027^\167", "\027ETB", "\027etb", "\030^\130", "\030\\030", "\030o030", "\030^\170", "\030CAN", "\030can", "\031^\131", "\031\\031", "\031o031", "\031^\171", "\031EM", "\031em", "\032^\132", "\032\\032", "\032o032", "\032^\172", "\032SUB", "\032sub", "\033^\133", "\033\\033", "\033o033", "\033^\173", "\033ESC", "\033esc", "\034^\134", "\034\\034", "\034o034", "\034^\174", "\034FS", "\034fs", "\035^\135", "\035\\035", "\035o035", "\035^\175", "\035GS", "\035gs", "\036^\136", "\036\\036", "\036o036", "\036^\176", "\036RS", "\036rs", "\037^\137", "\037\\037", "\037o037", "\037^\177", "\037US", "\037us", "\040space", "\040\\040", "\040o040", "\040\040", "\040SP", "\040SPACE", "\040sp", "\041\041", "\041\\041", "\041o041", "\042\042", "\042\\042", "\042o042", "\043\043", "\043\\043", "\043o043", "\044\044", "\044\\044", "\044o044", "\045\045", "\045\\045", "\045o045", "\046\046", "\046\\046", "\046o046", "\047\047", "\047\\047", "\047o047", "\050\050", "\050\\050", "\050o050", "\051\051", "\051\\051", "\051o051", "\052\052", "\052\\052", "\052o052", "\053\053", "\053\\053", "\053o053", "\054\054", "\054\\054", "\054o054", "\055\055", "\055\\055", "\055o055", "\056\056", "\056\\056", "\056o056", "\057\057", "\057\\057", "\057o057", "\060\060", "\060\\060", "\060o060", "\061\061", "\061\\061", "\061o061", "\062\062", "\062\\062", "\062o062", "\063\063", "\063\\063", "\063o063", "\064\064", "\064\\064", "\064o064", "\065\065", "\065\\065", "\065o065", "\066\066", "\066\\066", "\066o066", "\067\067", "\067\\067", "\067o067", "\070\070", "\070\\070", "\070o070", "\071\071", "\071\\071", "\071o071", "\072\072", "\072\\072", "\072o072", "\073\073", "\073\\073", "\073o073", "\074\074", "\074\\074", "\074o074", "\075\075", "\075\\075", "\075o075", "\076\076", "\076\\076", "\076o076", "\077\077", "\077\\077", "\077o077", "\100\100", "\100\\100", "\100o100", "\101\101", "\101\\101", "\101o101", "\102\102", "\102\\102", "\102o102", "\103\103", "\103\\103", "\103o103", "\104\104", "\104\\104", "\104o104", "\105\105", "\105\\105", "\105o105", "\106\106", "\106\\106", "\106o106", "\107\107", "\107\\107", "\107o107", "\110\110", "\110\\110", "\110o110", "\111\111", "\111\\111", "\111o111", "\112\112", "\112\\112", "\112o112", "\113\113", "\113\\113", "\113o113", "\114\114", "\114\\114", "\114o114", "\115\115", "\115\\115", "\115o115", "\116\116", "\116\\116", "\116o116", "\117\117", "\117\\117", "\117o117", "\120\120", "\120\\120", "\120o120", "\121\121", "\121\\121", "\121o121", "\122\122", "\122\\122", "\122o122", "\123\123", "\123\\123", "\123o123", "\124\124", "\124\\124", "\124o124", "\125\125", "\125\\125", "\125o125", "\126\126", "\126\\126", "\126o126", "\127\127", "\127\\127", "\127o127", "\130\130", "\130\\130", "\130o130", "\131\131", "\131\\131", "\131o131", "\132\132", "\132\\132", "\132o132", "\133\133", "\133\\133", "\133o133", "\134\134", "\134\\134", "\134o134", "\135\135", "\135\\135", "\135o135", "\136\136", "\136\\136", "\136o136", "\137\137", "\137\\137", "\137o137", "\140\140", "\140\\140", "\140o140", "\141\141", "\141\\141", "\141o141", "\142\142", "\142\\142", "\142o142", "\143\143", "\143\\143", "\143o143", "\144\144", "\144\\144", "\144o144", "\145\145", "\145\\145", "\145o145", "\146\146", "\146\\146", "\146o146", "\147\147", "\147\\147", "\147o147", "\150\150", "\150\\150", "\150o150", "\151\151", "\151\\151", "\151o151", "\152\152", "\152\\152", "\152o152", "\153\153", "\153\\153", "\153o153", "\154\154", "\154\\154", "\154o154", "\155\155", "\155\\155", "\155o155", "\156\156", "\156\\156", "\156o156", "\157\157", "\157\\157", "\157o157", "\160\160", "\160\\160", "\160o160", "\161\161", "\161\\161", "\161o161", "\162\162", "\162\\162", "\162o162", "\163\163", "\163\\163", "\163o163", "\164\164", "\164\\164", "\164o164", "\165\165", "\165\\165", "\165o165", "\166\166", "\166\\166", "\166o166", "\167\167", "\167\\167", "\167o167", "\170\170", "\170\\170", "\170o170", "\171\171", "\171\\171", "\171o171", "\172\172", "\172\\172", "\172o172", "\173\173", "\173\\173", "\173o173", "\174\174", "\174\\174", "\174o174", "\175\175", "\175\\175", "\175o175", "\176\176", "\176\\176", "\176o176", "\177DEL", "\177\\177", "\177o177", "\177^\077", "\177del", "\177DELETE", "\177delete", "\200^\340", "\200\\200", "\200o200", "\200^\300", "\201^\301", "\201\\201", "\201o201", "\201^\341", "\202^\302", "\202\\202", "\202o202", "\202^\342", "\203^\303", "\203\\203", "\203o203", "\203^\343", "\204^\304", "\204\\204", "\204o204", "\204^\344", "\205^\305", "\205\\205", "\205o205", "\205^\345", "\206^\306", "\206\\206", "\206o206", "\206^\346", "\207^\307", "\207\\207", "\207o207", "\207^\347", "\210^\310", "\210\\210", "\210o210", "\210^\350", "\211^\311", "\211\\211", "\211o211", "\211^\351", "\212^\312", "\212\\212", "\212o212", "\212^\352", "\213^\313", "\213\\213", "\213o213", "\213^\353", "\214^\314", "\214\\214", "\214o214", "\214^\354", "\215^\315", "\215\\215", "\215o215", "\215^\355", "\216^\316", "\216\\216", "\216o216", "\216^\356", "\217^\317", "\217\\217", "\217o217", "\217^\357", "\220^\320", "\220\\220", "\220o220", "\220^\360", "\221^\321", "\221\\221", "\221o221", "\221^\361", "\222^\322", "\222\\222", "\222o222", "\222^\362", "\223^\323", "\223\\223", "\223o223", "\223^\363", "\224^\324", "\224\\224", "\224o224", "\224^\364", "\225^\325", "\225\\225", "\225o225", "\225^\365", "\226^\326", "\226\\226", "\226o226", "\226^\366", "\227^\327", "\227\\227", "\227o227", "\227^\367", "\230^\330", "\230\\230", "\230o230", "\230^\370", "\231^\331", "\231\\231", "\231o231", "\231^\371", "\232^\332", "\232\\232", "\232o232", "\232^\372", "\233^\333", "\233\\233", "\233o233", "\233^\373", "\234^\334", "\234\\234", "\234o234", "\234^\374", "\235^\335", "\235\\235", "\235o235", "\235^\375", "\236^\336", "\236\\236", "\236o236", "\236^\376", "\237^\337", "\237\\237", "\237o237", "\237^\377", "\240nbsp", "\240\\240", "\240o240", "\240NBSP", "\241\241", "\241\\241", "\241o241", "\242\242", "\242\\242", "\242o242", "\243\243", "\243\\243", "\243o243", "\244\244", "\244\\244", "\244o244", "\245\245", "\245\\245", "\245o245", "\246\246", "\246\\246", "\246o246", "\247\247", "\247\\247", "\247o247", "\250\250", "\250\\250", "\250o250", "\251\251", "\251\\251", "\251o251", "\252\252", "\252\\252", "\252o252", "\253\253", "\253\\253", "\253o253", "\254\254", "\254\\254", "\254o254", "\255\255", "\255\\255", "\255o255", "\256\256", "\256\\256", "\256o256", "\257\257", "\257\\257", "\257o257", "\260\260", "\260\\260", "\260o260", "\261\261", "\261\\261", "\261o261", "\262\262", "\262\\262", "\262o262", "\263\263", "\263\\263", "\263o263", "\264\264", "\264\\264", "\264o264", "\265\265", "\265\\265", "\265o265", "\266\266", "\266\\266", "\266o266", "\267\267", "\267\\267", "\267o267", "\270\270", "\270\\270", "\270o270", "\271\271", "\271\\271", "\271o271", "\272\272", "\272\\272", "\272o272", "\273\273", "\273\\273", "\273o273", "\274\274", "\274\\274", "\274o274", "\275\275", "\275\\275", "\275o275", "\276\276", "\276\\276", "\276o276", "\277\277", "\277\\277", "\277o277", "\300\300", "\300\\300", "\300o300", "\301\301", "\301\\301", "\301o301", "\302\302", "\302\\302", "\302o302", "\303\303", "\303\\303", "\303o303", "\304\304", "\304\\304", "\304o304", "\305\305", "\305\\305", "\305o305", "\306\306", "\306\\306", "\306o306", "\307\307", "\307\\307", "\307o307", "\310\310", "\310\\310", "\310o310", "\311\311", "\311\\311", "\311o311", "\312\312", "\312\\312", "\312o312", "\313\313", "\313\\313", "\313o313", "\314\314", "\314\\314", "\314o314", "\315\315", "\315\\315", "\315o315", "\316\316", "\316\\316", "\316o316", "\317\317", "\317\\317", "\317o317", "\320\320", "\320\\320", "\320o320", "\321\321", "\321\\321", "\321o321", "\322\322", "\322\\322", "\322o322", "\323\323", "\323\\323", "\323o323", "\324\324", "\324\\324", "\324o324", "\325\325", "\325\\325", "\325o325", "\326\326", "\326\\326", "\326o326", "\327\327", "\327\\327", "\327o327", "\330\330", "\330\\330", "\330o330", "\331\331", "\331\\331", "\331o331", "\332\332", "\332\\332", "\332o332", "\333\333", "\333\\333", "\333o333", "\334\334", "\334\\334", "\334o334", "\335\335", "\335\\335", "\335o335", "\336\336", "\336\\336", "\336o336", "\337\337", "\337\\337", "\337o337", "\340\340", "\340\\340", "\340o340", "\341\341", "\341\\341", "\341o341", "\342\342", "\342\\342", "\342o342", "\343\343", "\343\\343", "\343o343", "\344\344", "\344\\344", "\344o344", "\345\345", "\345\\345", "\345o345", "\346\346", "\346\\346", "\346o346", "\347\347", "\347\\347", "\347o347", "\350\350", "\350\\350", "\350o350", "\351\351", "\351\\351", "\351o351", "\352\352", "\352\\352", "\352o352", "\353\353", "\353\\353", "\353o353", "\354\354", "\354\\354", "\354o354", "\355\355", "\355\\355", "\355o355", "\356\356", "\356\\356", "\356o356", "\357\357", "\357\\357", "\357o357", "\360\360", "\360\\360", "\360o360", "\361\361", "\361\\361", "\361o361", "\362\362", "\362\\362", "\362o362", "\363\363", "\363\\363", "\363o363", "\364\364", "\364\\364", "\364o364", "\365\365", "\365\\365", "\365o365", "\366\366", "\366\\366", "\366o366", "\367\367", "\367\\367", "\367o367", "\370\370", "\370\\370", "\370o370", "\371\371", "\371\\371", "\371o371", "\372\372", "\372\\372", "\372o372", "\373\373", "\373\\373", "\373o373", "\374\374", "\374\\374", "\374o374", "\375\375", "\375\\375", "\375o375", "\376\376", "\376\\376", "\376o376", "\377\377", "\377\\377", "\377o377" }; static int n_keynames = ARRAYSIZE(keynames); static int keyname_sort[ARRAYSIZE(keynames)]; static const unsigned char *official_keynames[256] = { "^\100", "^\101", "^\102", "^\103", "^\104", "^\105", "^\106", "^\107", "^\110", "^\111", "^\112", "^\113", "^\114", "^\115", "^\116", "^\117", "^\120", "^\121", "^\122", "^\123", "^\124", "^\125", "^\126", "^\127", "^\130", "^\131", "^\132", "ESC", "^\134", "^\135", "^\136", "^\137", "SP", "\041", "\042", "\043", "\044", "\045", "\046", "\047", "\050", "\051", "\052", "\053", "\054", "\055", "\056", "\057", "\060", "\061", "\062", "\063", "\064", "\065", "\066", "\067", "\070", "\071", "\072", "\073", "\074", "\075", "\076", "\077", "\100", "\101", "\102", "\103", "\104", "\105", "\106", "\107", "\110", "\111", "\112", "\113", "\114", "\115", "\116", "\117", "\120", "\121", "\122", "\123", "\124", "\125", "\126", "\127", "\130", "\131", "\132", "\133", "\134", "\135", "\136", "\137", "\140", "\141", "\142", "\143", "\144", "\145", "\146", "\147", "\150", "\151", "\152", "\153", "\154", "\155", "\156", "\157", "\160", "\161", "\162", "\163", "\164", "\165", "\166", "\167", "\170", "\171", "\172", "\173", "\174", "\175", "\176", "^\077", "^\340", "^\301", "^\302", "^\303", "^\304", "^\305", "^\306", "^\307", "^\310", "^\311", "^\312", "^\313", "^\314", "^\315", "^\316", "^\317", "^\320", "^\321", "^\322", "^\323", "^\324", "^\325", "^\326", "^\327", "^\330", "^\331", "^\332", "^\333", "^\334", "^\335", "^\336", "^\337", "NBSP", "\241", "\242", "\243", "\244", "\245", "\246", "\247", "\250", "\251", "\252", "\253", "\254", "\255", "\256", "\257", "\260", "\261", "\262", "\263", "\264", "\265", "\266", "\267", "\270", "\271", "\272", "\273", "\274", "\275", "\276", "\277", "\300", "\301", "\302", "\303", "\304", "\305", "\306", "\307", "\310", "\311", "\312", "\313", "\314", "\315", "\316", "\317", "\320", "\321", "\322", "\323", "\324", "\325", "\326", "\327", "\330", "\331", "\332", "\333", "\334", "\335", "\336", "\337", "\340", "\341", "\342", "\343", "\344", "\345", "\346", "\347", "\350", "\351", "\352", "\353", "\354", "\355", "\356", "\357", "\360", "\361", "\362", "\363", "\364", "\365", "\366", "\367", "\370", "\371", "\372", "\373", "\374", "\375", "\376", "\377" }; static SL chardisp[256]; static const unsigned char *default_wordchar = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ" "00112233445566778899__$$..--" "àÀáÁâÂãÃäÄåÅæÆçÇèÈéÉêÊëËìÌíÍîÎïÏðÐñÑòÒóÓôÔõÕöÖøØùÙúÚûÛüÜýÝþÞ"; /* forward declarations */ static PRIMINIT Prim[]; static int nPrim; static BINDINIT Bind[]; static int nBind; static PRIMINIT Prim_qr[]; static int nPrim_qr; static BINDINIT Bind_qr[]; static int nBind_qr; static unsigned char self_insert_init[]; static int n_self_insert_init; static const unsigned char *chardisp_init[256]; #if 0 static void dbglog(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void dbglog(const char *fmt, ...) { va_list ap; static FILE *f = 0; if (! f) { f = fopen("ecl.fxn-tree.debug","w"); if (! f) return; } va_start(ap,fmt); vfprintf(f,fmt,ap); va_end(ap); fflush(f); } #endif static void ttyerr(void) { ttyerrs ++; if (ttyerrs > 20) { fprintf(eclerr(),"\r\nExcessive errors, exiting\r\n\n\n"); exit(1); } } static int wordchar(unsigned char ch) { if (! cpvalid) { int i; void cb(unsigned char lc, unsigned char uc) { charprop[lc].flags |= CPF_WORDCHAR; charprop[lc].ucmap = uc; charprop[uc].flags |= CPF_WORDCHAR; charprop[uc].lcmap = lc; } for (i=ARRAYSIZE(charprop)-1;i>=0;i--) { charprop[i].flags &= ~CPF_WORDCHAR; charprop[i].ucmap = i; charprop[i].lcmap = i; } if (ecl_scan_wordchars(&cb)) { for (i=0;default_wordchar[i]&&default_wordchar[i+1];i+=2) { cb(default_wordchar[i],default_wordchar[i+1]); } } cpvalid = 1; } return((charprop[ch].flags&CPF_WORDCHAR)?1:0); } static int digitchar(unsigned char ch) { return((charprop[ch].flags&CPF_DIGIT)?1:0); } static int spacechar(unsigned char ch) { return((charprop[ch].flags&CPF_SPACE)?1:0); } static NAMEHASH namehash(const char *name) { int i; NAMEHASH h; h = 1; for (i=0;name[i];i++) h = (((h & 0x1fffffff) << 3) | ((h >> 29) & 7)) + (unsigned char)name[i]; return(h); } static int fxncmp(NAMEHASH h, const char *n, FXN *f) { if (h < f->namehash) return(-1); if (h > f->namehash) return(1); return(strcmp(n,f->name)); } static FXN *sort_fxns_list(FXN *list) { FXN *a; FXN *b; FXN *t; FXN **p; if (!list || !list->l) return(list); a = 0; b = 0; while (list) { t = list; list = t->l; t->l = a; a = b; b = t; } a = sort_fxns_list(a); b = sort_fxns_list(b); p = &list; while (a || b) { if (a && (!b || (fxncmp(a->namehash,a->name,b) < 0))) { t = a; a = t->l; } else { t = b; b = t->l; } *p = t; p = &t->l; } *p = 0; return(list); } static FXN *fxns_list_to_tree(FXN *list) { int n; FXN *f; FXN *treeify(FXN *list, int n) { int nl; int i; FXN *f; FXN *r; if (! list) return(0); switch (n) { case 1: list->l = 0; list->r = 0; return(list); break; case 2: list->r = list->l; list->l = 0; list->r->l = 0; list->r->r = 0; return(list); break; } if (n < 3) abort(); nl = n / 2; for (i=nl-1,f=list;i>0;i--,f=f->l) ; r = f->l; f->l = 0; r->r = treeify(r->l,n-nl-1); r->l = treeify(list,nl); return(r); } for (n=0,f=list;f;n++,f=f->l) ; return(treeify(list,n)); } static FXN *fxns_tree_to_list(FXN *tree) { FXN *list; void append_tree(FXN *t) { if (t == 0) return; append_tree(t->l); append_tree(t->r); t->l = list; list = t; } list = 0; append_tree(tree); return(list); } static FXN *drop_fxn_from_list(FXN *list, FXN *todrop) { FXN *rv; FXN *f; rv = 0; while (list) { f = list; list = f->l; if (f != todrop) { f->l = rv; rv = f; } } return(rv); } static FXN *add_fxn_to_tree(FXN *toadd, FXN *tree) { toadd->l = fxns_tree_to_list(tree); return(fxns_list_to_tree(sort_fxns_list(toadd))); } static void check_fxn_tree(FXN *root) { FXN *prev; void walk_inorder(FXN *n) { if (! n) return; walk_inorder(n->l); if (prev && (fxncmp(prev->namehash,prev->name,n) >= 0)) abort(); prev = n; walk_inorder(n->r); } prev = 0; walk_inorder(root); } static FXN *fxn_by_name(FXN *root, const char *n) { FXN *f; int h; int c; check_fxn_tree(root); h = namehash(n); f = root; while (1) { if (! f) return(0); c = fxncmp(h,n,f); if (c < 0) { f = f->l; } else if (c > 0) { f = f->r; } else { return(f); } } } static KEYMAP *new_keymap(void) { KEYMAP *km; int i; if (km_free) { km = km_free; km_free = km->freelink; } else { km = malloc(sizeof(KEYMAP)); km->alllink = km_all; km_all = km; } km->refcnt = 1; km->name = 0; km->fxns = 0; km->freelink = 0; for (i=NMAPKEYS-1;i>=0;i--) km->keys[i].type = BINDING_UNBOUND; return(km); } static void fxn_use(FXN *f) { f->usecnt ++; if (f->usecnt <= 0) abort(); } static void fxn_unuse(FXN *f) { f->usecnt --; if (f->usecnt < 0) abort(); } static void keymap_ref(KEYMAP *km) { km->refcnt ++; if (km->refcnt <= 0) abort(); } static void bind_unbind(BINDING *); /* forward */ static void keymap_deref(KEYMAP *km) { int i; km->refcnt --; if (km->refcnt > 0) return; if (km->refcnt < 0) abort(); for (i=NMAPKEYS-1;i>=0;i--) bind_unbind(&km->keys[i]); free(km->name); km->refcnt = -10; /* anything s.t. refcnt+1 is -ve */ km->name = 0; km->freelink = km_free; km_free = km; } static void bind_unbind(BINDING *b) { switch (b->type) { case BINDING_UNBOUND: break; case BINDING_FXN: fxn_unuse(b->fxn); b->type = BINDING_UNBOUND; break; case BINDING_KEYMAP: keymap_deref(b->keymap); b->type = BINDING_UNBOUND; break; default: abort(); break; } } static int keyname_search(const unsigned char *kn) { int l; int m; int h; l = -1; h = n_keynames; while (h-l > 1) { m = (l + h) / 2; if (strcmp(keynames[keyname_sort[m]]+1,kn) <= 0) l = m; else h = m; } return(keyname_sort[l]); } static KSEQ key_sequence(const unsigned char *seq0) { const unsigned char *seq; int l; static unsigned char *keys = 0; static int ka = 0; int kl; const unsigned char *kn; kl = 0; seq = seq0; while (1) { if (! *seq) return((KSEQ){.status=KS_MISSINGKEY,.keyoff=seq-seq0}); l = keyname_search(seq); kn = keynames[l]; l = strlen(kn+1); if (strncmp(kn+1,seq,l)) return((KSEQ){.status=KS_BADKEYNAME,.keyoff=seq-seq0}); if (kl >= ka) keys = realloc(keys,(ka=kl+8)*sizeof(*keys)); keys[kl++] = kn[0]; seq += l; switch (*seq) { default: return((KSEQ){.status=KS_BADKEYNAME,.keyoff=seq-seq0}); break; case '\0': goto break_seq; break; case '-': seq ++; break; } } break_seq:; if (kl < 1) abort(); return((KSEQ){.status=KS_OK,.seq=keys,.seqlen=kl}); } static BINDFIND find_binding(KEYMAP *in, const unsigned char *seq0) { int i; BINDING *b; KSEQ ks; ks = key_sequence(seq0); switch (ks.status) { case KS_BADKEYNAME: return((BINDFIND){.status=BFS_BADKEYNAME,.keyoff=ks.keyoff}); break; case KS_MISSINGKEY: return((BINDFIND){.status=BFS_MISSINGKEY,.keyoff=ks.keyoff}); break; case KS_OK: break; default: abort(); break; } if (ks.seqlen < 1) abort(); i = 0; while (1) { b = &in->keys[ks.seq[i]]; if (i < ks.seqlen-1) { if (b->type != BINDING_KEYMAP) { bind_unbind(b); b->type = BINDING_KEYMAP; b->keymap = new_keymap(); } in = b->keymap; } else { return((BINDFIND){.status=BFS_FOUND,.found={.map=in,.key=ks.seq[i]}}); } i ++; } } static void init_binding(KEYMAP *km, const char *seq, const char *fxn) { FXN *f; BINDFIND bf; BINDING *b; f = fxn_by_name(km->fxns,fxn); if (! f) { fprintf(eclerr(),"warning: initialization: function `%s' not found\n",fxn); return; } bf = find_binding(km,seq); switch (bf.status) { case BFS_BADKEYNAME: fprintf(eclerr(),"warning: initialization: bad key name %s at %s\n",seq,seq+bf.keyoff); return; break; case BFS_MISSINGKEY: fprintf(eclerr(),"warning: initialization: missing key name %s at %s\n",seq,seq+bf.keyoff); return; break; case BFS_FOUND: b = &bf.found.map->keys[bf.found.key]; bind_unbind(b); b->type = BINDING_FXN; b->fxn = f; fxn_use(f); break; default: abort(); break; } } static void init_linestate(LINESTATE *ls) { ls->b = 0; ls->a = 0; ls->l = 0; ls->c = 0; ls->m = -1; } static void free_linestate(LINESTATE *ls) { free(ls->b); } static void init_hist(HISTSTATE *h) { h->lines = 0; h->size = 0; h->h = 0; h->t = 0; h->n = 0; h->ss = 0; h->ssa = 0; h->ssl = 0; h->ro = 0; h->keepempty = 1; } static FXN *new_fxn(const char *name) { FXN *f; f = malloc(sizeof(FXN)); f->name = strdup(name); f->namehash = namehash(name); f->type = FXN_UNSET; f->usecnt = 0; return(f); } static void setup_prims(KEYMAP *k, const PRIMINIT *pi, int npi) { int i; FXN *f; for (i=npi-1;i>=0;i--) { f = new_fxn(pi[i].name); f->type = FXN_PRIMITIVE; f->primitive = malloc(sizeof(PRIMITIVE)); f->primitive->fxn = f; f->primitive->impl = pi[i].impl; f->primitive->flags = 0; if (pi[i].wantschar) f->primitive->flags |= PRIMF_WANTSCHAR; f->l = k->fxns; k->fxns = f; } k->fxns = fxns_list_to_tree(sort_fxns_list(k->fxns)); } static void init_ecl(void) { FXN *f; int i; #define CMP(a,b) \ strcmp(keynames[keyname_sort[(a)]]+1,keynames[keyname_sort[(b)]]+1) void knheap(int x, int n) { int l; int r; int s; int t; while (1) { l = HL(x); r = HR(x); if ((l < n) && (CMP(x,l) < 0)) { if ((r < n) && (CMP(x,r) < 0)) { s = (CMP(l,r) > 0) ? l : r; } else { s = l; } } else { if ((r < n) && (CMP(x,r) < 0)) { s = r; } else { return; } } t = keyname_sort[x]; keyname_sort[x] = keyname_sort[s]; keyname_sort[s] = t; x = s; } } #undef CMP km_free = 0; km_all = 0; left_a = 0; left_l = 0; left_p = 0; left_b = 0; init_linestate(&desl); init_linestate(&ddl); init_linestate(&cdl); km_root = new_keymap(); km_root->name = strdup("root"); setup_prims(km_root,&Prim[0],nPrim); km_qr = new_keymap(); km_qr->name = strdup("qr"); setup_prims(km_qr,&Prim_qr[0],nPrim_qr); km_lnext = new_keymap(); for (i=256-1;i>=0;i--) chardisp[i] = (SL){.s=strdup(chardisp_init[i]),.l=strlen(chardisp_init[i])}; for (i=n_keynames-1;i>=0;i--) keyname_sort[i] = i; for (i=HU(n_keynames-1);i>=0;i--) knheap(i,n_keynames); for (i=n_keynames-1;i>0;i--) { int t; t = keyname_sort[0]; keyname_sort[0] = keyname_sort[i]; keyname_sort[i] = t; knheap(0,i); } for (i=nBind-1;i>=0;i--) init_binding(km_root,Bind[i].seq,Bind[i].fxn); for (i=n_self_insert_init-1;i>=0;i--) { init_binding(km_root,official_keynames[self_insert_init[i]],"self-insert"); } for (i=nBind_qr-1;i>=0;i--) init_binding(km_qr,Bind_qr[i].seq,Bind_qr[i].fxn); f = fxn_by_name(km_root->fxns,"self-insert"); if (f == 0) abort(); for (i=NMAPKEYS-1;i>=0;i--) { km_lnext->keys[i].type = BINDING_FXN; km_lnext->keys[i].fxn = f; fxn_use(f); } init_hist(&hist); init_hist(&killring); for (i=ARRAYSIZE(charprop)-1;i>=0;i--) charprop[i].flags = 0; for (i=0;i<10;i++) charprop[(int)("0123456789"[i])].flags |= CPF_DIGIT; charprop[0x09].flags |= CPF_SPACE; charprop[0x20].flags |= CPF_SPACE; charprop[0xa0].flags |= CPF_SPACE; ecl_path_init(); statestack = 0; qr_cur = 0; histsearch_string.s = 0; histsearch_string.l = 0; macstack = 0; km_top = km_root; keymap_ref(km_top); km_cur = km_root; keymap_ref(km_cur); initted = 1; } static int get_tty_state(TTYSTATE *ts) { if (GETATTR(&ts->tio) < 0) return(1); return(0); } static int set_tty_state(const TTYSTATE *ts) { if (SETATTR(&ts->tio) < 0) return(1); return(0); } static int save_tty(void) { if (! tty_state_saved) { if (get_tty_state(&o_ts)) return(1); tty_state_saved = 1; } n_ts = o_ts; n_ts.tio.c_cc[VINTR] = _POSIX_VDISABLE; n_ts.tio.c_cc[VQUIT] = _POSIX_VDISABLE; n_ts.tio.c_cc[VERASE] = _POSIX_VDISABLE; n_ts.tio.c_cc[VKILL] = _POSIX_VDISABLE; n_ts.tio.c_cc[VEOF] = _POSIX_VDISABLE; n_ts.tio.c_cc[VEOL] = _POSIX_VDISABLE; #ifdef VEOL2 n_ts.tio.c_cc[VEOL2] = _POSIX_VDISABLE; #endif #ifdef VSWTCH n_ts.tio.c_cc[VSWTCH] = _POSIX_VDISABLE; #endif #ifdef VLNEXT n_ts.tio.c_cc[VLNEXT] = _POSIX_VDISABLE; #endif #ifdef VWERASE n_ts.tio.c_cc[VWERASE] = _POSIX_VDISABLE; #endif #ifdef VRPRNT n_ts.tio.c_cc[VRPRNT] = _POSIX_VDISABLE; #endif #ifdef VREPRINT n_ts.tio.c_cc[VREPRINT] = _POSIX_VDISABLE; #endif #ifdef VFLUSHO n_ts.tio.c_cc[VFLUSHO] = _POSIX_VDISABLE; #endif #ifdef VSUSP n_ts.tio.c_cc[VSUSP] = _POSIX_VDISABLE; #endif #ifdef VDSUSP n_ts.tio.c_cc[VDSUSP] = _POSIX_VDISABLE; #endif #ifdef VLNEXT n_ts.tio.c_cc[VLNEXT] = _POSIX_VDISABLE; #endif #ifdef VDISCARD n_ts.tio.c_cc[VDISCARD] = _POSIX_VDISABLE; #endif #ifdef VSTATUS n_ts.tio.c_cc[VSTATUS] = _POSIX_VDISABLE; #endif #ifdef VSTOP n_ts.tio.c_cc[VSTOP] = CTRL_('S'); #endif #ifdef VSTART n_ts.tio.c_cc[VSTART] = CTRL_('Q'); #endif n_ts.tio.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|ECHOKE|ECHOCTL); #ifdef XCASE n_ts.tio.c_lflag &= ~(XCASE); #endif n_ts.tio.c_iflag &= ~(INLCR|IGNCR|ICRNL); n_ts.tio.c_cc[VMIN] = 0; n_ts.tio.c_cc[VTIME] = 0; if (ecl_quittable()) { n_ts.tio.c_cc[VINTR] = _POSIX_VDISABLE; n_ts.tio.c_cc[VQUIT] = CTRL_('\\'); } else { n_ts.tio.c_lflag &= ~ISIG; } if (set_tty_state(&n_ts)) return(1); return(0); } static void tty_ring_save(unsigned char ch) { tty_ring[tty_ring_h] = ch; tty_ring_h = (tty_ring_h + 1) % TTYRINGSIZE; if (tty_ring_h == tty_ring_t) tty_ring_t = (tty_ring_t + 1) % TTYRINGSIZE; } static int tty_read(unsigned char *chp, int nb) { int rv; int e; if (tty_pushed) { *chp = tty_push; tty_pushed = 0; return(1); } if (nb) { if (n_ts.tio.c_cc[VMIN]) { n_ts.tio.c_cc[VMIN] = 0; set_tty_state(&n_ts); } rv = read(fd,chp,1); if (rv == 0) { errno = EWOULDBLOCK; rv = -1; } if (rv == 1) tty_ring_save(*chp); return(rv); } else { if (n_ts.tio.c_cc[VMIN] != 1) { n_ts.tio.c_cc[VMIN] = 1; set_tty_state(&n_ts); } rv = read(fd,chp,1); e = errno; n_ts.tio.c_cc[VMIN] = 0; set_tty_state(&n_ts); errno = e; if (rv == 1) tty_ring_save(*chp); return(rv); } } static void linestate_space(LINESTATE *ls, int n) { if (ls->a < n) { ls->b = realloc(ls->b,ls->a=n); } } static void copy_to_ls(LINESTATE *ls, int at, const void *data, int len) { linestate_space(ls,at+len); bcopy(data,ls->b+at,len); } static void reprompt(void) { if (statestack) { (*statestack->ops->reprompt)(statestack->cookie); } else { ecl_reprompt(); } } static int update_line(void) { int i; SL *d; int common; void n_spaces(int n) { for (;n>0;n--) putc(' ',eclout()); } void n_backspaces(int n) { for (;n>0;n--) putc('\b',eclout()); } void cursor_to(int x) { if (x < cdl.c) { n_backspaces(cdl.c-x); } else if (x > cdl.c) { fwrite(ddl.b+cdl.c,1,x-cdl.c,eclout()); bcopy(ddl.b+cdl.c,cdl.b+cdl.c,x-cdl.c); if (x > cdl.l) cdl.l = x; } cdl.c = x; } if (desl.c > desl.l) abort(); if (full_redraw) { full_redraw = 0; fprintf(eclout(),"\n"); reprompt(); fflush(eclout()); cdl.l = 0; cdl.c = 0; } ddl.l = 0; for (i=0;;i++) { if (i == desl.m) { if (mark_display_len) { copy_to_ls(&ddl,ddl.l,mark_display,mark_display_len); ddl.l += mark_display_len; } } if (i == desl.c) ddl.c = ddl.l; if (i >= desl.l) break; d = &chardisp[desl.b[i]]; copy_to_ls(&ddl,ddl.l,d->s,d->l); ddl.l += d->l; } linestate_space(&cdl,ddl.l); common = (ddl.l < cdl.l) ? ddl.l : cdl.l; for (i=0;(i cdl.l) { cursor_to(ddl.l); } else { for (i=ddl.l-1;(i>=0)&&(ddl.b[i]==cdl.b[i]);i--) ; if (i < 0) abort(); cursor_to(i+1); } } cursor_to(ddl.c); return(1); } static void maybe_resize_history(HISTSTATE *h, int newsize) { HISTSTATE new; int i; int j; int k; if (newsize == h->size) return; if (newsize < 1) { for ( i = h->t, j = h->n; j > 0; i = HIST_NEXT(h,i), j-- ) { free(h->lines[i].s); } free(h->lines); h->lines = 0; h->size = 0; h->h = 0; h->t = 0; h->n = 0; return; } new = *h; /* copy miscellaneous fields */ new.lines = malloc(newsize*sizeof(SL)); new.size = newsize; new.h = 0; new.t = 0; new.n = 0; if (new.size >= h->n) { i = h->t; j = h->n; } else { i = HIST_SUB(h,h->h,new.size); j = new.size; for (k=h->t;k!=i;k=HIST_NEXT(h,k)) free(h->lines[k].s); } for (;j>0;i=HIST_NEXT(h,i),j--) { new.lines[new.h] = h->lines[i]; new.h = HIST_NEXT(&new,new.h); new.n ++; } free(h->lines); *h = new; } static void save_to_history(HISTSTATE *h, const unsigned char *data, int len) { SL sl; if (h->ro || (h->size < 1)) return; if ((len == 0) && !h->keepempty) return; if ((h->n < 0) || (h->n > h->size)) abort(); if (h->n == h->size) { free(h->lines[h->t].s); h->t = HIST_NEXT(h,h->t); h->n --; } sl.s = malloc(len); bcopy(data,sl.s,len); sl.l = len; h->lines[h->h] = sl; h->h = HIST_NEXT(h,h->h); h->n ++; } static void beep(void) { putc('\7',eclout()); } void ecl_beep(void) { beep(); } void ecl_full_redraw(void) { full_redraw = 1; } static void mac_ref(MACRO *m) { m->refcnt ++; if (m->refcnt <= 0) abort(); } static void mac_deref(MACRO *m) { int i; m->refcnt --; if (m->refcnt < 0) abort(); if (m->refcnt > 0) return; switch (m->type) { default: abort(); break; case MAC_F: for (i=m->size-1;i>=0;i--) fxn_unuse(m->fxns[i]); break; case MAC_K: break; } free(m->keys); free(m->fxns); free(m); } static void start_macro(MACRO *m) { MACSTACK *s; s = malloc(sizeof(MACSTACK)); s->link = macstack; macstack = s; s->mac = m; s->at = 0; m->disabled = 1; mac_ref(m); } static void end_macro(void) { MACSTACK *s; s = macstack; macstack = s->link; s->mac->disabled =0; mac_deref(s->mac); free(s); } static void set_km_top(KEYMAP *km) { if (km == km_top) return; keymap_ref(km); keymap_deref(km_top); km_top = km; } static void set_km_cur(KEYMAP *km) { if (km == km_cur) return; keymap_ref(km); keymap_deref(km_cur); km_cur = km; } int ecl_read(int argfd, char *buf, int siz) { BINDING *b; FXN *f; fd = argfd; if (! initted) init_ecl(); if (left_p < left_l) { int n; n = left_l - left_p; if (siz < n) n = siz; bcopy(left_b+left_p,buf,n); left_p += n; return(n); } if (save_tty()) return(read(fd,buf,siz)); ttyerrs = 0; desl.l = 0; desl.c = 0; desl.m = -1; cdl.l = 0; cdl.c = 0; set_km_top(km_root); set_km_cur(km_root); mode = MODE_NORMAL; cpvalid = 0; maybe_resize_history(&hist,ecl_history_size()); maybe_resize_history(&killring,ecl_killring_size()); hist.keepempty = ecl_keep_empty_history(); free(mark_display); mark_display = ecl_mark_display(); mark_display_len = mark_display ? strlen(mark_display) : 0; tty_pushed = 0; full_redraw = 0; /* XXX get max line width */ lastck = CK_UNSPECIAL; while (1) { unsigned char ch; int n; fflush(eclout()); switch (mode) { case MODE_NORMAL: f = 0; if (macstack) { if (macstack->at >= macstack->mac->size) { end_macro(); continue; } switch (macstack->mac->type) { default: abort(); break; case MAC_F: ch = macstack->mac->keys[macstack->at]; f = macstack->mac->fxns[macstack->at]; break; case MAC_K: ch = macstack->mac->keys[macstack->at]; break; } macstack->at ++; } else { n = tty_read(&ch,1); if ((n < 0) && (errno == EWOULDBLOCK)) { if (update_line()) continue; n = tty_read(&ch,0); if ((n < 0) && (errno == EWOULDBLOCK)) { int f; f = fcntl(fd,F_GETFL,0); if (f & O_NONBLOCK) { fcntl(fd,F_SETFL,f&~O_NONBLOCK); continue; } errno = EWOULDBLOCK; } } if (n < 0) { fprintf(eclerr(),"\r\nRead error %d\r\n",errno); ttyerr(); continue; } else if (n == 0) { fprintf(eclerr(),"\r\nRead EOF\r\n"); ttyerr(); continue; } ttyerrs = 0; /* At this point, I'd like to do if (ch > NMAPKEYS) continue; but gcc produces a warning ("always false due to limited range of data type") if I do that - even if I explicitly cast both sides to int(!!). I consider this a bug, but I'm not ambitious enough to try to fix it myself. */ } if (! f) { b = &km_cur->keys[ch]; switch (b->type) { case BINDING_UNBOUND: set_km_cur(km_top); lastck = CK_UNSPECIAL; beep(); break; case BINDING_FXN: set_km_cur(km_top); f = b->fxn; break; case BINDING_KEYMAP: set_km_cur(b->keymap); break; } } if (f) { switch (f->type) { default: abort(); break; case FXN_PRIMITIVE: last_key_struck = ch; thisck = CK_UNSPECIAL; (*f->primitive->impl)(); lastck = thisck; break; case FXN_MACRO: if (f->macro->disabled) { fprintf(eclerr(),"\nRecursive call to macro %s\n",f->macro->fxn->name); ecl_full_redraw(); } else { start_macro(f->macro); } break; } } break; case MODE_RETURN: save_to_history(&hist,desl.b,desl.l); desl.c = desl.l; update_line(); fprintf(eclout(),"\n"); fflush(eclout()); copy_to_ls(&desl,desl.l,"\n",1); desl.l ++; n = (desl.l < siz) ? desl.l : siz; if (n > 0) bcopy(desl.b,buf,n); if (n < desl.l) { left_l = desl.l - n; if (left_l > left_a) { free(left_b); left_b = malloc(left_a=left_l); } bcopy(desl.b+n,left_b,left_l); left_p = 0; } set_tty_state(&o_ts); tty_state_saved = tty_state_locked; return(n); break; } } } void ecl_locktty_changed(int setp) { if (setp) { tty_state_locked = 1; } else { tty_state_locked = 0; tty_state_saved = 0; } } static void delete_n_at(int n, int at, int saveit) { if ( (at < 0) || (at > desl.l) || (n < 0) || (n > desl.l) || (at+n > desl.l) ) abort(); desl.l -= n; if (saveit) { if ( (lastck == CK_DELETE) && (killring.n > 0) && (at <= last_del_at) && (at+n >= last_del_at) ) { unsigned char *buf; SL *kl; if (n > 0) { kl = &killring.lines[HIST_PREV(&killring,killring.h)]; buf = malloc(kl->l+n); if (kl->l) bcopy(kl->s,buf+last_del_at-at,kl->l); if (last_del_at > at) { bcopy(desl.b+at,buf,last_del_at-at); } if (at+n > last_del_at) { bcopy( desl.b + last_del_at, buf + (last_del_at-at) + kl->l, at + n - last_del_at ); } free(kl->s); kl->s = buf; kl->l += n; } } else { save_to_history(&killring,desl.b+at,n); } } if (desl.c > at+n) desl.c -= n; else if (desl.c > at) desl.c = at; if (desl.m > at+n) desl.m -= n; else if (desl.m > at) desl.m = at; if ((n > 0) && (desl.l > at)) bcopy(desl.b+at+n,desl.b+at,desl.l-at); if (saveit) { last_del_n = n; last_del_at = at; } } static void insert_n_at(int n, int at, const unsigned char *data) { if ((at < 0) || (at > desl.l) || (n < 0)) abort(); if (n < 1) return; linestate_space(&desl,desl.l+n); if (at < desl.l) bcopy(desl.b+at,desl.b+at+n,desl.l-at); bcopy(data,desl.b+at,n); desl.l += n; if (desl.m > at) desl.m += n; if (desl.c >= at) desl.c += n; } static void erase_history_temp(HISTSTATE *h) { if ((h->at < 0) && (h->at != HA_TCSH_GAP)) abort(); delete_n_at(h->insn,h->insat,0); } static void insert_history_temp(HISTSTATE *h) { if (h->at < 0) abort(); h->insat = desl.c; h->insn = h->lines[h->at].l; insert_n_at(h->lines[h->at].l,desl.c,h->lines[h->at].s); } static void history_ring_newer(HISTSTATE *h, CMDKIND ck) { if (lastck != ck) { h->at = HA_AFTER_NEWEST; } else if (h->at >= 0) { erase_history_temp(h); } switch (h->at) { case HA_AFTER_NEWEST: beep(); h->at = HA_BEFORE_OLDEST; break; case HA_BEFORE_OLDEST: if (h->n < 1) { beep(); } else { h->at = h->t; insert_history_temp(h); } break; default: h->at = HIST_NEXT(h,h->at); if (h->at == h->h) { h->at = HA_AFTER_NEWEST; } else { insert_history_temp(h); } break; } thisck = ck; } static void history_ring_older(HISTSTATE *h, CMDKIND ck) { if (lastck != ck) { h->at = HA_AFTER_NEWEST; } else if (h->at >= 0) { erase_history_temp(h); } switch (h->at) { case HA_BEFORE_OLDEST: beep(); h->at = HA_AFTER_NEWEST; break; case HA_AFTER_NEWEST: if (h->n < 1) { beep(); } else { h->at = HIST_PREV(h,h->h); insert_history_temp(h); } break; default: if (h->at == h->t) { h->at = HA_BEFORE_OLDEST; } else { h->at = HIST_PREV(h,h->at); insert_history_temp(h); } break; } thisck = ck; } static void hsave(HISTSTATE *h) { int n; n = desl.l - desl.c; if (n > h->ssa) { free(h->ss); h->ssa = n; h->ss = malloc(n); } bcopy(desl.b+desl.c,h->ss,n); h->ssl = n; delete_n_at(n,desl.c,0); } static void history_ring_newer_prefix(HISTSTATE *h, CMDKIND ck) { int first; if (lastck != ck) { h->at = HA_AFTER_NEWEST; hsave(h); } else if ((h->at >= 0) || (h->at == HA_TCSH_GAP)) { erase_history_temp(h); } switch (h->at) { case HA_AFTER_NEWEST: case HA_BEFORE_OLDEST: if (h->n < 1) { beep(); break; } /* fall through */ case HA_TCSH_GAP: h->at = HIST_PREV(h,h->t); first = 1; if (0) { default: first = 0; } while (1) { h->at = HIST_NEXT(h,h->at); if ((h->at == h->h) && !first) { h->at = HA_TCSH_GAP; h->insat = desl.c; h->insn = h->ssl; insert_n_at(h->ssl,desl.c,h->ss); desl.c = h->insat; beep(); break; } if ( (h->lines[h->at].l >= desl.c) && !bcmp(h->lines[h->at].s,desl.b,desl.c) ) { h->insat = desl.c; h->insn = h->lines[h->at].l - desl.c; insert_n_at(h->lines[h->at].l-desl.c,desl.c,h->lines[h->at].s+desl.c); desl.c = h->insat; break; } first = 0; } break; } thisck = ck; } static void history_ring_older_prefix(HISTSTATE *h, CMDKIND ck) { int first; if (lastck != ck) { h->at = HA_AFTER_NEWEST; hsave(h); } else if ((h->at >= 0) || (h->at == HA_TCSH_GAP)) { erase_history_temp(h); } switch (h->at) { case HA_BEFORE_OLDEST: case HA_AFTER_NEWEST: if (h->n < 1) { beep(); break; } /* fall through */ case HA_TCSH_GAP: h->at = h->h; first = 1; if (0) { default: first = 0; } while (1) { if ((h->at == h->t) && !first) { h->at = HA_TCSH_GAP; h->insat = desl.c; h->insn = h->ssl; insert_n_at(h->ssl,desl.c,h->ss); desl.c = h->insat; beep(); break; } h->at = HIST_PREV(h,h->at); if ( (h->lines[h->at].l >= desl.c) && !bcmp(h->lines[h->at].s,desl.b,desl.c) ) { h->insat = desl.c; h->insn = h->lines[h->at].l - desl.c; insert_n_at(h->lines[h->at].l-desl.c,desl.c,h->lines[h->at].s+desl.c); desl.c = h->insat; break; } first = 0; } break; } thisck = ck; } static int word_fiddle_setup(int *startp, int *endp, int (*chartest)(unsigned char)) { int x; if (desl.l < 1) return(1); x = desl.c; while ((x > 0) && !((x < desl.l) && (*chartest)(desl.b[x]))) x --; if ((x < desl.l) && (*chartest)(desl.b[x])) { while ((x > 0) && (*chartest)(desl.b[x])) x --; if ((x < desl.l) && !(*chartest)(desl.b[x])) x ++; } else { while ((x < desl.l) && !(*chartest)(desl.b[x])) x ++; if (x >= desl.l) return(1); } *startp = x; while ((x < desl.l) && (*chartest)(desl.b[x])) x ++; *endp = x; return(0); } static void word_fiddle(void (*cb)(int)) { int a; int b; if (word_fiddle_setup(&a,&b,&wordchar)) return; for (;a 0) && !spacechar(desl.b[x-1])) x --; y = x; while ((y < desl.l) && !spacechar(desl.b[y])) y ++; if (x == y) { delete_n_at(0,0,1); ecl_path_len = 0; return; } y -= x; ecl_path_space(y); q = '\0'; j = 0; for (i=0;i= a) b = realloc(b,a=l+8); b[l++] = ch; } void flush(void) { for (;i0': case '?': case '[': case ']': case '{': case '|': case '}': case '~': case 127 ... 159: case '\'': case '"': if (0) { case '$': case '`': c.f |= QCHF_NODQ; } c.f |= QCHF_QUOTE; v[i] = c; nq ++; break; } } else { if (i0 >= 0) flush(); append(c.c); } } if (i0 >= 0) flush(); insert_n_at(l,desl.c,b); pathlen = l; free(b); } static void path_reinsert(void) { reinsert_qchv(ecl_path_buf,ecl_path_len); } static int get_key_sequence(KEYMAP *m, unsigned char **kbp, int *klp, BINDING **bp) { int n; unsigned char ch; unsigned char *b; int l; BINDING *binding; b = 0; l = 0; while (1) { n = tty_read(&ch,0); if (n < 0) { fprintf(eclerr(),"\r\nRead error %d\r\n",errno); ttyerr(); free(b); return(1); } else if (n == 0) { fprintf(eclerr(),"\r\nRead EOF\r\n"); ttyerr(); free(b); return(1); } l ++; if (kbp) { b = realloc(b,l); b[l-1] = ch; } binding = &m->keys[ch]; if (binding->type != BINDING_KEYMAP) break; m = binding->keymap; } if (kbp) *kbp = b; if (klp) *klp = l; if (bp) *bp = binding; return(0); } static void key_sequence_name(int (*get)(int), void (*each)(unsigned char)) { int i; const unsigned char *kn; int k; for (i=0;;i++) { k = (*get)(i); if (k < 0) break; if (i) (*each)('-'); if (k >= 256) abort(); for (kn=official_keynames[k];*kn;kn++) (*each)(*kn); } } static void walk_bindtree(KEYMAP *m, void (*cb)(const unsigned char *, int, BINDING *)) { unsigned char *b; int a; int l; void setkey(int last, unsigned char key) { if (last >= a) b = realloc(b,a=last+8); b[last] = key; l = last + 1; } void walk(KEYMAP *m) { int ol; int i; BINDING *binding; ol = l; for (i=0;ikeys[i]; if (binding->type == BINDING_KEYMAP) { walk(binding->keymap); } else { (*cb)(b,l,binding); } } } b = 0; a = 0; l = 0; walk(m); free(b); } static KEYMAP *keymap_by_name(const char *name) { KEYMAP *km; for (km=km_all;km;km=km->alllink) { if (km->name && !strcmp(name,km->name)) return(km); } return(0); } void ecl_bind(char **v) { __label__ ret; int vx; KEYMAP *rootkm; FXN *ofinterest; FILE *eo; FILE *ee; int nprinted; auto void fail(const char *, ...) __attribute__((__format__(__printf__,1,2))); void fail(const char *fmt, ...) { va_list ap; fprintf(ee,"%s: ",v[0]); va_start(ap,fmt); vfprintf(ee,fmt,ap); va_end(ap); goto ret; } int all(FXN *f __attribute__((__unused__))) { return(1); } int one(FXN *f) { return(f==ofinterest); } void putc_eo(unsigned char c) { putc(c,eo); } void putc_ee(unsigned char c) { putc(c,ee); } void scan_bindings(int (*interest)(FXN *)) { FXN *lastf; unsigned char *lastk; int lastnk; unsigned char firstk; void flushlast(void) { int getfirst(int x) { if (x < 0) abort(); if (x >= lastnk) return(-1); if (x == lastnk-1) return(firstk); return(lastk[x]); } int getlast(int x) { if (x < 0) abort(); if (x >= lastnk) return(-1); return(lastk[x]); } if (lastnk < 0) return; if (lastnk < 1) abort(); if (firstk == lastk[lastnk-1]) { key_sequence_name(&getlast,&putc_eo); } else { key_sequence_name(&getfirst,&putc_eo); fprintf(eo," .. "); key_sequence_name(&getlast,&putc_eo); } fprintf(eo,"\t%s\n",lastf->name); nprinted ++; lastnk = -1; } void fn(const unsigned char *keys, int nkeys, BINDING *b) { FXN *f; switch (b->type) { default: abort(); break; case BINDING_UNBOUND: return; break; case BINDING_FXN: f = b->fxn; break; } if (! (*interest)(f)) return; if ( (f == lastf) && (nkeys == lastnk) && ( (nkeys < 2) || !bcmp(keys,lastk,nkeys-1) ) && (keys[nkeys-1] == lastk[lastnk-1]+1) ) { lastk[lastnk-1] = keys[nkeys-1]; } else { flushlast(); free(lastk); lastnk = nkeys; lastk = malloc(nkeys); bcopy(keys,lastk,nkeys); firstk = keys[nkeys-1]; lastf = f; } } lastk = 0; lastnk = -1; lastf = 0; walk_bindtree(rootkm,&fn); flushlast(); } if (! initted) init_ecl(); nprinted = 0; eo = eclout(); ee = eclerr(); rootkm = km_root; vx = 1; while (1) { void needarg(const char *what) { fail("%s needs another argument\n",what); } if (!v[vx] || (v[vx][0] != '-')) break; if (! strcmp(v[vx],"-k")) { KEYMAP *k; if (! v[vx+1]) needarg("-k"); k = keymap_by_name(v[vx+1]); if (! k) fail("no keymap `%s' defined\n",v[vx+1]); rootkm = k; vx += 2; } else if (! strcmp(v[vx],"--")) { vx ++; break; } else { fail("bad option `%s'\n",v[vx]); } } if (! v[vx]) { scan_bindings(&all); } else if (! v[vx+1]) { ofinterest = fxn_by_name(rootkm->fxns,v[vx]); if (ofinterest) { scan_bindings(&one); if (nprinted == 0) { fprintf(ee,"%s: nothing is bound to %s\n",v[0],ofinterest->name); } } else { KSEQ ks; ks = key_sequence(v[vx]); if (ks.status == KS_OK) { KEYMAP *m; BINDING *b; int i; int get(int x) { if (x < 0) abort(); if (x > i) return(-1); return(ks.seq[x]); } m = rootkm; for (i=0;ikeys[ks.seq[i]]; if (b->type == BINDING_KEYMAP) { if (i == ks.seqlen-1) { fprintf(ee,"%s: ",v[0]); key_sequence_name(&get,&putc_ee); fprintf(ee," is a prefix, not a complete sequence\n"); } else { m = b->keymap; } } else { if (i == ks.seqlen-1) { switch (b->type) { case BINDING_UNBOUND: fprintf(ee,"%s: ",v[0]); key_sequence_name(&get,&putc_ee); fprintf(ee," is not bound to anything\n"); break; case BINDING_FXN: fprintf(eo,"%s\n",b->fxn->name); break; default: abort(); break; } } else { fprintf(ee,"%s: ",v[0]); key_sequence_name(&get,&putc_ee); fprintf(ee," is a complete sequence, not a prefix\n"); break; } } } } else { fprintf(ee,"%s: %s is neither a key sequence nor a function name\n",v[0],v[vx]); } } } else if (! v[vx+2]) { FXN *f; BINDFIND bf; BINDING *b; f = fxn_by_name(rootkm->fxns,v[vx+1]); if (! f) fail("%s is not a function name\n",v[vx+1]); bf = find_binding(rootkm,v[vx]); switch (bf.status) { case BFS_BADKEYNAME: fprintf(ee,"%s: bad key name at %s\n",v[vx],v[vx]+bf.keyoff); break; case BFS_MISSINGKEY: fprintf(ee,"%s: missing key name\n",v[vx]); break; case BFS_FOUND: b = &bf.found.map->keys[bf.found.key]; bind_unbind(b); b->type = BINDING_FXN; b->fxn = f; fxn_use(f); break; default: abort(); break; } } else { fprintf(ee,"%s: Too many arguments.\n",v[0]); } ret:; for (vx=0;v[vx];vx++) free(v[vx]); free(v); } void ecl_disp(char **v) { __label__ ret; int vx; FILE *eo; FILE *ee; int i; int n; int dflag; auto void fail(const char *, ...) __attribute__((__format__(__printf__,1,2))); void fail(const char *fmt, ...) { va_list ap; fprintf(ee,"%s: ",v[0]); va_start(ap,fmt); vfprintf(ee,fmt,ap); va_end(ap); goto ret; } void default_disp(unsigned char c) { free(chardisp[c].s); chardisp[c] = (SL){.s=strdup(chardisp_init[c]),.l=strlen(chardisp_init[c])}; } void show_disp(unsigned char c) { fprintf(eo,"%3d ",c); fwrite(chardisp[c].s,1,chardisp[c].l,eo); fprintf(eo,"\n"); } void set_disp(unsigned char c, const char *ds) { free(chardisp[c].s); chardisp[c] = (SL){.s=strdup(ds),.l=strlen(ds)}; } if (! initted) init_ecl(); eo = eclout(); ee = eclerr(); vx = 1; dflag = 0; while (1) { void needarg(const char *what) { fail("%s needs another argument\n",what); } if (!v[vx] || (v[vx][0] != '-')) break; if (! strcmp(v[vx],"--")) { vx ++; break; } else if (! strcmp(v[vx],"-d")) { vx ++; dflag = 1; } else { fail("bad option `%s'\n",v[vx]); } } if (dflag) { if (! v[vx]) { for (i=0;i<256;i++) default_disp(i); } else { for (i=vx;v[i];i++) { n = atoi(v[i]); if (n != (unsigned char)n) { fprintf(ee,"%s: %d out of range\n",v[0],n); } else { default_disp(n); } } } } else { if (! v[vx]) { for (i=0;i<256;i++) show_disp(i); } else if (! v[vx+1]) { n = atoi(v[vx]); if (n != (unsigned char)n) { fprintf(ee,"%s: %d out of range\n",v[0],n); } else { show_disp(n); } } else if (! v[vx+2]) { n = atoi(v[vx]); if (n != (unsigned char)n) { fprintf(ee,"%s: %d out of range\n",v[0],n); } else { set_disp(n,v[vx+1]); } } else { fprintf(ee,"%s: Too many arguments.\n",v[0]); } } ret:; for (vx=0;v[vx];vx++) free(v[vx]); free(v); } static void remove_macro(FXN *f) { if (f->type != FXN_MACRO) abort(); mac_deref(f->macro); km_root->fxns = fxns_list_to_tree( sort_fxns_list( drop_fxn_from_list( fxns_tree_to_list(km_root->fxns), f))); } static void walk_fxns(FXN *t, void (*cb)(FXN *)) { if (! t) return; walk_fxns(t->l,cb); (*cb)(t); walk_fxns(t->r,cb); } static void show_macro(MACRO *m) { FILE *eo; FXN *f; FXN *lastf; int i; unsigned char *kv; int kl; int ka; void addk(unsigned char k) { if (kl >= ka) kv = realloc(kv,ka=kl+8); kv[kl++] = k; } void flushf(void) { int i; int j; unsigned char knb[2]; if (lastf == 0) return; fprintf(eo," %s",lastf->name); knb[1] = '\0'; for (i=kl-1;i>=0;i--) { knb[0] = kv[i]; j = keyname_search(&knb[0]); if ((keynames[j][1] != kv[i]) || keynames[j][2]) { int get(int x) { if ((x < 0) || (x > kl)) abort(); if (x == kl) return(-1); return(kv[x]); } void each(unsigned char c) { putc(c,eo); } putc('{'/*}*/,eo); key_sequence_name(&get,&each); putc(/*{*/'}',eo); return; } } putc('[',eo); fwrite(kv,1,kl,eo); putc(']',eo); lastf = 0; } eo = eclout(); fprintf(eo,"%s =",m->fxn?(const char *)m->fxn->name:"(no name)"); kv = 0; ka = 0; kl = 0; switch (m->type) { default: abort(); break; case MAC_F: lastf = 0; for (i=0;isize;i++) { f = m->fxns[i]; if (f == lastf) { addk(m->keys[i]); } else { flushf(); if ( (f->type == FXN_PRIMITIVE) && (f->primitive->flags & PRIMF_WANTSCHAR) ) { lastf = f; addk(m->keys[i]); } else { fprintf(eo," %s",f->name); } } } flushf(); break; case MAC_K: break; } fprintf(eo,"\n"); } void ecl_macro(char **v) { __label__ ret; int vx; FILE *eo; FILE *ee; int i; int j; int k; MACTYPE type; int undefine; auto void fail(const char *, ...) __attribute__((__format__(__printf__,1,2))); void fail(const char *fmt, ...) { va_list ap; fprintf(ee,"%s: ",v[0]); va_start(ap,fmt); vfprintf(ee,fmt,ap); va_end(ap); goto ret; } void putc_ee(unsigned char c) { putc(c,ee); } if (! initted) init_ecl(); eo = eclout(); ee = eclerr(); vx = 1; type = MAC_F; undefine = 0; while (1) { void needarg(const char *what) { fail("%s needs another argument\n",what); } if (!v[vx] || (v[vx][0] != '-')) break; if (! strcmp(v[vx],"--")) { vx ++; break; } else if (! strcmp(v[vx],"-f")) { vx ++; type = MAC_F; } else if (! strcmp(v[vx],"-k")) { vx ++; type = MAC_K; } else if (! strcmp(v[vx],"-u")) { vx ++; undefine = 1; } else { fail("bad option `%s'\n",v[vx]); } } if (undefine) { for (i=vx;v[i];i++) { FXN *f; f = fxn_by_name(km_root->fxns,v[i]); if (! f) { fprintf(ee,"%s: %s: not found\n",v[0],v[i]); } else if (f->type != FXN_MACRO) { fprintf(ee,"%s: %s: not a macro\n",v[0],v[i]); } else if (f->usecnt > 0) { fprintf(ee,"%s: %s: busy\n",v[0],v[i]); } else { remove_macro(f); } } } else { if (! v[vx]) { void fn(FXN *f) { if (f->type == FXN_MACRO) show_macro(f->macro); } walk_fxns(km_root->fxns,&fn); } else if (! v[vx+1]) { FXN *f; f = fxn_by_name(km_root->fxns,v[vx]); if (! f) { fprintf(ee,"%s: %s: not found\n",v[0],v[i]); } else if (f->type != FXN_MACRO) { fprintf(ee,"%s: %s: not a macro\n",v[0],v[i]); } else { show_macro(f->macro); } } else { MACRO *m; char *name; FXN *f; FXN *mf; KSEQ ks; KEYMAP *km; BINDING *b; char *lb; int new; name = v[vx]; vx ++; mf = fxn_by_name(km_root->fxns,name); if (mf) { if (mf->type == FXN_PRIMITIVE) fail("%s: can't redefine a primitive\n",name); } else { mf = 0; } for (i=0;v[vx+i];i++) ; m = malloc(sizeof(MACRO)); m->fxn = 0; m->type = type; m->refcnt = 1; m->disabled =0; m->size = i; switch (type) { default: abort(); break; case MAC_F: m->keys = malloc(i); m->fxns = malloc(i*sizeof(FXN *)); for (i=m->size-1;i>=0;i--) m->fxns[i] = 0; k = 0; for (i=0;isize;i++) { void neither(void) { mac_deref(m); fail("%s: neither a key sequence nor a function\n",v[vx+i]); } void set(unsigned char ch, FXN *f) { if ((k < 0) || (k >= m->size)) abort(); m->keys[k] = ch; m->fxns[k] = f; fxn_use(f); k ++; } void multichar(const unsigned char *kv, int kl) { if (kl < 1) { m->size --; return; } if (kl == 1) { set(kv[0],f); return; } m->keys = realloc(m->keys,m->size+kl-1); m->fxns = realloc(m->fxns,(m->size+kl-1)*sizeof(FXN *)); for (j=m->size+kl-2;j>=m->size;j--) m->fxns[j] = 0; m->size += kl - 1; for (j=0;j j) return(-1); return(ks.seq[x]); } b = &km->keys[ks.seq[j]]; switch (b->type) { default: abort(); break; case BINDING_UNBOUND: mac_deref(m); fprintf(ee,"%s: %s: ",v[0],v[vx+i]); key_sequence_name(&kget,&putc_ee); fprintf(ee,": not bound\n"); break; case BINDING_FXN: if (j == ks.seqlen-1) { set(ks.seq[j],b->fxn); goto continue_args; } else { mac_deref(m); fprintf(ee,"%s: %s: ",v[0],v[vx+i]); key_sequence_name(&kget,&putc_ee); fprintf(ee," is bound, not a prefix\n"); } break; case BINDING_KEYMAP: if (j == ks.seqlen-1) { mac_deref(m); fprintf(ee,"%s: %s: ",v[0],v[vx+i]); key_sequence_name(&kget,&putc_ee); fprintf(ee," is a prefix, not a complete sequence\n"); } else { km = b->keymap; } break; } } abort(); break; case KS_BADKEYNAME: case KS_MISSINGKEY: break; } f = fxn_by_name(km_root->fxns,v[vx+i]); if (f) { set(0,f); goto continue_args; } j = strlen(v[vx+i]); lb = index(v[vx+i],'['); if (lb && (v[vx+i][j-1] == ']')) { multichar(lb+1,(v[vx+i]+j-1)-(lb+1)); goto continue_args; } lb = index(v[vx+i],'{'/*}*/); if (lb && (v[vx+i][j-1] == /*{*/'}')) { v[vx+i][j-1] = '\0'; ks = key_sequence(lb+1); v[vx+i][j-1] = /*{*/'}'; switch (ks.status) { default: abort(); break; case KS_OK: multichar(ks.seq,ks.seqlen); break; case KS_BADKEYNAME: mac_deref(m); fail("%s: bad key name\n",v[vx+i]); break; case KS_MISSINGKEY: mac_deref(m); fail("%s: missing key name\n",v[vx+i]); break; } goto continue_args; } neither(); } continue_args:; break; case MAC_K: m->keys = malloc(i); m->fxns = 0; for (i=0;isize;i++) { ks = key_sequence(v[vx+i]); switch (ks.status) { default: abort(); break; case KS_OK: if (ks.seqlen != 1) { case KS_BADKEYNAME: case KS_MISSINGKEY: fail("bad key name %s\n",v[vx+i]); } break; } m->keys[i] = ks.seq[0]; } break; } if (mf) { switch (mf->type) { default: abort(); break; case FXN_MACRO: mac_deref(mf->macro); new = 0; break; } } else { mf = new_fxn(name); mf->type = FXN_MACRO; new = 1; } mf->macro = m; m->fxn = mf; if (new) km_root->fxns = add_fxn_to_tree(mf,km_root->fxns); } } ret:; for (vx=0;v[vx];vx++) free(v[vx]); free(v); } static void tcsh_to_control(void) { if ((lastck == CK_TCSHHIST) && (hist.at != HA_TCSH_GAP)) { hist.insn += hist.insat; hist.insat = 0; lastck = CK_HISTORY; } } static void push_statestack(const SSOPS *ops) { STATESTACK *ss; ss = malloc(sizeof(STATESTACK)); ss->line = desl; init_linestate(&desl); ss->mode = mode; ss->hist_ro = hist.ro; ss->hist_at = hist.at; keymap_ref(km_top); ss->topkm = km_top; ss->ops = ops; ss->link = statestack; statestack = ss; ss->cookie = (*ops->init)(); } static void *pop_statestack(void) { STATESTACK *ss; void *rv; ss = statestack; statestack = ss->link; free_linestate(&desl); desl = ss->line; mode = ss->mode; hist.ro = ss->hist_ro; hist.at = ss->hist_at; set_km_top(ss->topkm); set_km_cur(km_top); rv = ss->cookie; keymap_deref(ss->topkm); free(ss); return(rv); } static void reset_line(void) { desl.l = 0; desl.c = 0; desl.m = -1; } static int sl_contains(SL big, SL small) { int maxbx; int bx; if (small.l < 1) return(1); if (small.l > big.l) return(0); maxbx = big.l - small.l + 1; bx = 0; while (bx < maxbx) { unsigned char *p; p = memchr(big.s+bx,small.s[0],maxbx-bx); if (! p) return(0); if (! bcmp(p+1,small.s+1,small.l-1)) return(1); bx = (p+1) - big.s; } return(0); } static void *qr_init(void) { QRSTATE *s; s = malloc(sizeof(QRSTATE)); s->stage = QRS_STR_A; s->a.s = 0; s->a.l = 0; s->b.s = 0; s->b.l = 0; s->prevtop = 0; ecl_full_redraw(); return(s); } static void qr_free(QRSTATE *s) { free(s->a.s); free(s->b.s); if (s->prevtop) keymap_deref(s->prevtop); free(s); } static void qr_done(void) { if (qr_cur->nfound < 1) beep(); desl.c = (qr_cur->fromc < 0) ? desl.l : qr_cur->fromc; set_km_top(qr_cur->prevtop); set_km_cur(km_top); qr_free(qr_cur); qr_cur = 0; } static int qr_findnext(void) { int i; while (1) { if (desl.c+qr_cur->a.l > desl.l) { qr_done(); return(1); } for (i=0;ia.l;i++) { if (qr_cur->a.s[i] != desl.b[desl.c+i]) { desl.c ++; goto continue_search; } } qr_cur->nfound ++; return(0); continue_search:; } } static void qr_finish(void *sv) { QRSTATE *s; s = sv; switch (s->stage) { default: abort(); break; case QRS_STR_A: s->a.l = desl.l; s->a.s = malloc(desl.l); bcopy(desl.b,s->a.s,desl.l); s->stage = QRS_STR_B; reset_line(); ecl_full_redraw(); break; case QRS_STR_B: s->b.l = desl.l; s->b.s = malloc(desl.l); bcopy(desl.b,s->b.s,desl.l); s->stage = QRS_QUERY; pop_statestack(); if (desl.c == desl.l) { desl.c = 0; s->fromc = -1; } else { s->fromc = desl.c; } keymap_ref(km_top); s->prevtop = km_top; s->nfound = 0; set_km_top(km_qr); set_km_cur(km_qr); qr_cur = s; qr_findnext(); ecl_full_redraw(); break; } } static void qr_undo_show(void) { if (qr_cur->stage == QRS_SHOW) { delete_n_at(qr_cur->b.l,desl.c,0); insert_n_at(qr_cur->a.l,desl.c,qr_cur->a.s); desl.c -= qr_cur->a.l; desl.m = qr_cur->showmark; qr_cur->stage = QRS_QUERY; } } static void qr_replace(void) { if (qr_cur->stage != QRS_SHOW) { delete_n_at(qr_cur->a.l,desl.c,0); insert_n_at(qr_cur->b.l,desl.c,qr_cur->b.s); } qr_cur->stage = QRS_QUERY; } static void qr_show(void) { if (qr_cur->stage != QRS_SHOW) { qr_cur->showmark = desl.m; delete_n_at(qr_cur->a.l,desl.c,0); insert_n_at(qr_cur->b.l,desl.c,qr_cur->b.s); desl.c -= qr_cur->b.l; qr_cur->stage = QRS_SHOW; } } static void qr_abort(void *sv) { pop_statestack(); beep(); qr_free(sv); ecl_full_redraw(); } static void qr_reprompt(void *sv) { QRSTATE *s; FILE *eo; int i; s = sv; eo = eclout(); switch (s->stage) { default: abort(); break; case QRS_STR_A: fprintf(eo,"Replace "); break; case QRS_STR_B: fprintf(eo,"Replace "); for (i=0;ia.l;i++) { SL *d; d = &chardisp[s->a.s[i]]; fwrite(d->s,1,d->l,eo); } fprintf(eo, " with "); break; } } static const SSOPS qr_ops = { &qr_init, &qr_finish, &qr_abort, &qr_reprompt }; static void *hs_init(void) { CMDKIND *k; k = malloc(sizeof(CMDKIND)); *k = lastck; ecl_full_redraw(); return(k); } static void hs_finish(void *kv) { lastck = *(CMDKIND *)kv; free(kv); if (desl.l > 0) { free(histsearch_string.s); histsearch_string.l = desl.l; histsearch_string.s = malloc(desl.l); bcopy(desl.b,histsearch_string.s,desl.l); } pop_statestack(); ecl_full_redraw(); if (histsearch_string.l < 1) { fprintf(eclerr(),"No previous search string\n"); thisck = lastck; return; } if (lastck != CK_HISTORY) { hist.at = HA_AFTER_NEWEST; } else if (hist.at >= 0) { erase_history_temp(&hist); } thisck = CK_HISTORY; if (hist.n < 1) { beep(); return; } while (1) { switch (histsearch_dir) { default: abort(); break; case HD_NEW_TO_OLD: switch (hist.at) { case HA_AFTER_NEWEST: case HA_BEFORE_OLDEST: case HA_TCSH_GAP: hist.at = HIST_PREV(&hist,hist.h); break; default: if (hist.at == hist.t) { hist.at = HA_BEFORE_OLDEST; beep(); return; } hist.at = HIST_PREV(&hist,hist.at); break; } break; case HD_OLD_TO_NEW: switch (hist.at) { case HA_AFTER_NEWEST: case HA_BEFORE_OLDEST: case HA_TCSH_GAP: hist.at = hist.t; break; default: hist.at = HIST_NEXT(&hist,hist.at); if (hist.at == hist.h) { hist.at = HA_AFTER_NEWEST; beep(); return; } break; } break; } if (sl_contains(hist.lines[hist.at],histsearch_string)) { insert_history_temp(&hist); return; } } } static void hs_abort(void *kv) { thisck = *(CMDKIND *)kv; pop_statestack(); free(kv); ecl_full_redraw(); } static void hs_reprompt(void *kv __attribute__((__unused__))) { switch (histsearch_dir) { default: abort(); break; case HD_NEW_TO_OLD: fprintf(eclout(),"Search: "); break; case HD_OLD_TO_NEW: fprintf(eclout(),"hcraeS: "); break; } } static const SSOPS hs_ops = { &hs_init, &hs_finish, &hs_abort, &hs_reprompt }; static void P_backward_character(void) { if (desl.c > 0) desl.c --; } static void P_backward_word(void) { while ( (desl.c > 0) && ( !wordchar(desl.b[--desl.c]) || ( (desl.c > 0) && wordchar(desl.b[desl.c-1]) ) ) ); } static void P_beginning_of_line(void) { desl.c = 0; } static void P_delete_next_character(void) { if (desl.c < desl.l) delete_n_at(1,desl.c,1); thisck = CK_DELETE; } static void P_delete_previous_character(void) { if (desl.c > 0) delete_n_at(1,desl.c-1,1); thisck = CK_DELETE; } static void P_end_of_line(void) { desl.c = desl.l; } static void P_finish_line(void) { if (statestack) { (*statestack->ops->finish)(statestack->cookie); } else { mode = MODE_RETURN; } } static void P_abort(void) { if (statestack) { (*statestack->ops->abort)(statestack->cookie); } else { beep(); } } static void P_forward_character(void) { if (desl.c < desl.l) desl.c ++; } static void P_forward_word(void) { while ( (desl.c < desl.l) && ( !wordchar(desl.b[desl.c++]) || ( (desl.c < desl.l) && wordchar(desl.b[desl.c]) ) ) ); } static void P_delete_next_word(void) { int c; c = desl.c; P_forward_word(); delete_n_at(desl.c-c,c,1); thisck = CK_DELETE; } static void P_delete_previous_word(void) { int c; c = desl.c; P_backward_word(); delete_n_at(c-desl.c,desl.c,1); thisck = CK_DELETE; } static void P_kill_entire_line(void) { delete_n_at(desl.l,0,1); thisck = CK_DELETE; } static void P_kill_region(void) { switch (lastck) { case CK_HISTORY: erase_history_temp(&hist); return; break; case CK_YANK: erase_history_temp(&killring); return; break; case CK_PATH: delete_n_at(desl.c-pathlen,pathlen,1); thisck = CK_DELETE; return; break; case CK_TCSHHIST: erase_history_temp(&hist); return; break; default: break; } if (desl.m < 0) { beep(); return; } if (desl.m < desl.c) { delete_n_at(desl.c-desl.m,desl.m,1); } else { delete_n_at(desl.m-desl.c,desl.c,1); } desl.m = -1; thisck = CK_DELETE; } static void P_kill_to_end_of_line(void) { delete_n_at(desl.l-desl.c,desl.c,1); thisck = CK_DELETE; } static void P_noop(void) { } static void P_redraw_line(void) { ecl_full_redraw(); } static void P_self_insert(void) { insert_n_at(1,desl.c,&last_key_struck); } static void P_transpose_after(void) { int x; unsigned char c; if (desl.l < 2) return; x = desl.c; if (desl.l-x < 2) return; c = desl.b[x]; desl.b[x] = desl.b[x+1]; desl.b[x+1] = c; } static void P_transpose_around(void) { int x; unsigned char c; if (desl.l < 2) return; x = desl.c; if ((desl.l-x < 1) || (x < 1)) return; c = desl.b[x-1]; desl.b[x-1] = desl.b[x]; desl.b[x] = c; } static void P_transpose_before(void) { int x; unsigned char c; if (desl.l < 2) return; x = desl.c; if (x < 2) return; c = desl.b[x-2]; desl.b[x-2] = desl.b[x-1]; desl.b[x-1] = c; } static void P_transpose_near_after(void) { int x; unsigned char c; if (desl.l < 2) return; x = desl.c; if (desl.l-x < 2) x = desl.l - 2; c = desl.b[x]; desl.b[x] = desl.b[x+1]; desl.b[x+1] = c; } static void P_transpose_near_around(void) { int x; unsigned char c; if (desl.l < 2) return; x = desl.c; if (desl.l-x < 1) x = desl.l - 1; if (x < 1) x = 1; c = desl.b[x-1]; desl.b[x-1] = desl.b[x]; desl.b[x] = c; } static void P_transpose_near_before(void) { int x; unsigned char c; if (desl.l < 2) return; x = desl.c; if (x < 2) x = 2; c = desl.b[x-2]; desl.b[x-2] = desl.b[x-1]; desl.b[x-1] = c; } static void P_set_mark(void) { desl.m = desl.c; } static void P_history_control_N(void) { tcsh_to_control(); history_ring_newer(&hist,CK_HISTORY); } static void P_history_control_P(void) { tcsh_to_control(); history_ring_older(&hist,CK_HISTORY); } static void P_literal_next(void) { set_km_cur(km_lnext); } static void P_yank(void) { if (killring.n < 1) { beep(); return; } killring.at = HIST_PREV(&killring,killring.h); insert_history_temp(&killring); thisck = CK_YANK; } static void P_ring_yank(void) { if (lastck == CK_PATH) { delete_n_at(pathlen,desl.c-pathlen,0); } history_ring_older(&killring,CK_YANK); } static void P_ring_yank_reverse(void) { history_ring_newer(&killring,CK_YANK); } static void P_case_word_capitalize(void) { int first; void cb(int x) { desl.b[x] = first ? charprop[desl.b[x]].ucmap : charprop[desl.b[x]].lcmap; first = 0; } first = 1; word_fiddle(&cb); } static void P_case_word_lower(void) { void cb(int x) { desl.b[x] = charprop[desl.b[x]].lcmap; } word_fiddle(&cb); } static void P_case_word_upper(void) { void cb(int x) { desl.b[x] = charprop[desl.b[x]].ucmap; } word_fiddle(&cb); } static void P_number_inc(void) { int a; int b; if (word_fiddle_setup(&a,&b,&digitchar)) { insert_n_at(1,desl.c,"0"); return; } while (b > a) { b --; if (desl.b[b] == '9') { desl.b[b] = '0'; } else { desl.b[b] ++; return; } } insert_n_at(1,a,"1"); } static void P_number_dec(void) { int a; int b; int i; if (word_fiddle_setup(&a,&b,&digitchar)) { insert_n_at(1,desl.c,"0"); return; } do { for (i=a;i= a) b = realloc(b,a=l+8); b[l++] = c; } if (get_key_sequence(km_top,&kb,&kl,0)) return; b = 0; a = 0; l = 0; key_sequence_name(&get,&each); free(kb); insert_n_at(l,desl.c,b); free(b); } static void P_key_binding(void) { BINDING *b; if (get_key_sequence(km_top,0,0,&b)) return; switch (b->type) { default: abort(); break; case BINDING_UNBOUND: insert_n_at(7,desl.c,"nothing"); break; case BINDING_FXN: insert_n_at(strlen(b->fxn->name),desl.c,b->fxn->name); break; } } static void P_follow_symlink(void) { path_fiddle_setup(1); ecl_path_follow_symlink(); path_reinsert(); thisck = CK_PATH; } static void P_tcsh_history_n(void) { history_ring_newer_prefix(&hist,CK_TCSHHIST); } static void P_tcsh_history_p(void) { history_ring_older_prefix(&hist,CK_TCSHHIST); } static void P_brace_complete(void) { path_fiddle_setup(1); ecl_path_brace_complete(); path_reinsert(); thisck = CK_PATH; } static void P_hardpathify(void) { path_fiddle_setup(1); ecl_path_harden(); path_reinsert(); thisck = CK_PATH; } static void P_query_replace(void) { push_statestack(&qr_ops); } static void P__qr_all(void) { qr_replace(); while (! qr_findnext()) qr_replace(); } static void P__qr_yes(void) { qr_replace(); qr_findnext(); } static void P__qr_no(void) { qr_undo_show(); desl.c ++; qr_findnext(); } static void P__qr_yes_stop(void) { qr_replace(); qr_done(); } static void P__qr_no_stop(void) { qr_undo_show(); qr_done(); } static void P__qr_show(void) { qr_show(); } static void P_history_search_forward(void) { histsearch_dir = HD_NEW_TO_OLD; push_statestack(&hs_ops); } static void P_history_search_reverse(void) { histsearch_dir = HD_OLD_TO_NEW; push_statestack(&hs_ops); } static void P_dollarsign_expand(void) { int a; int b; char *n; char **v; int i; int varchar(unsigned char ch) { switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '_': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': return(1); break; } return(0); } if (word_fiddle_setup(&a,&b,&varchar)) return; if (b-a < 1) { beep(); return; } n = malloc(b-a+1); bcopy(desl.b+a,n,b-a); n[b-a] = '\0'; v = ecl_dollarsign_value(n); free(n); if (! v) { beep(); } else { delete_n_at(b-a,a,1); if (v[0]) { QCH *qv; int ql; int qa; void qv_append(const unsigned char *s, int f) { int j; j = strlen(s); ql += j; if (ql > qa) qv = realloc(qv,(qa=ql+16)*sizeof(QCH)); for (j=0;s[j];j++) qv[j] = (QCH){.f=f,.c=s[j]}; } qv = 0; ql = 0; qa = 0; if (! v[1]) { qv_append(v[0],QCHF_MAYQUOTE); } else { int i; qv_append("{"/*}*/,0); for (i=0;v[i];i++) { if (i) qv_append(",",0); qv_append(v[i],QCHF_MAYQUOTE); } qv_append(/*{*/"}",0); } reinsert_qchv(qv,ql); free(qv); } for (i=0;v[i];i++) free(v[i]); free(v); } } static void P_debug_info_1(void) { FILE *eo; FXN *last; void walk(FXN *f, void (*each)(FXN *)) { if (! f) return; walk(f->l,each); (*each)(f); walk(f->r,each); } void fn(FXN *f) { if (last && (fxncmp(f->namehash,f->name,last) <= 0)) { fprintf(eo,"last %08x %s, this %08x %s\n", last->namehash,last->name,f->namehash,f->name); } last = f; } eo = eclout(); last = 0; fprintf(eo,"\nFunction name disorder check:\n"); walk(km_root->fxns,&fn); if (desl.c > 0) { int tat; void print_at(int at) { switch (at) { case HA_BEFORE_OLDEST: fprintf(eo,"HA_BEFORE_OLDEST"); break; case HA_AFTER_NEWEST: fprintf(eo,"HA_AFTER_NEWEST"); break; case HA_TCSH_GAP: fprintf(eo,"HA_TCSH_GAP"); break; default: fprintf(eo,"%d",at); break; } } fprintf(eo,"lastck = "); switch (lastck) { case CK_UNSPECIAL: fprintf(eo,"CK_UNSPECIAL"); break; case CK_HISTORY: fprintf(eo,"CK_HISTORY"); break; case CK_DELETE: fprintf(eo,"CK_DELETE"); break; case CK_YANK: fprintf(eo,"CK_YANK"); break; case CK_PATH: fprintf(eo,"CK_PATH"); break; case CK_TCSHHIST: fprintf(eo,"CK_TCSHHIST"); break; default: fprintf(eo,"%d",(int)lastck); break; } fprintf(eo,"\nhist: at = "); print_at(hist.at); if ((hist.at >= 0) || (hist.at == HA_TCSH_GAP)) { fprintf(eo," (temp = %d@%d)",hist.insn,hist.insat); } fprintf(eo,", h = %d, t = %d, n = %d\n",hist.h,hist.t,hist.n); fprintf(eo,"Search:\n"); tat = hist.at; if (lastck != CK_TCSHHIST) tat = HA_AFTER_NEWEST; if (tat < 0) tat = hist.h; print_at(tat); while (1) { if (tat == hist.t) { fprintf(eo," tail"); break; } tat = HIST_PREV(&hist,tat); fprintf(eo," "); print_at(tat); if ( (hist.lines[tat].l >= desl.c) && !bcmp(hist.lines[tat].s,desl.b,desl.c) ) { fprintf(eo," found"); break; } } fprintf(eo,"\n"); } ecl_full_redraw(); thisck = lastck; } static void P_debug_info_2(void) { fprintf(eclout(),"\r\nNothing here yet\r\n"); ecl_full_redraw(); thisck = lastck; } static void P_path_next(void) { path_fiddle_setup(1); ecl_path_next(); path_reinsert(); thisck = CK_PATH; } static void P_path_prev(void) { path_fiddle_setup(1); ecl_path_prev(); path_reinsert(); thisck = CK_PATH; } static PRIMINIT Prim[] = { { "abort", &P_abort, 0 }, { "backward-character", &P_backward_character, 0 }, { "backward-word", &P_backward_word, 0 }, { "beginning-of-line", &P_beginning_of_line, 0 }, { "brace-complete", &P_brace_complete, 0 }, { "case-word-capitalize", &P_case_word_capitalize, 0 }, { "case-word-lower", &P_case_word_lower, 0 }, { "case-word-upper", &P_case_word_upper, 0 }, { "complete-alter", &P_complete_alter, 0 }, { "complete-preserve", &P_complete_preserve, 0 }, { "debug-info-1", &P_debug_info_1, 0 }, { "debug-info-2", &P_debug_info_2, 0 }, { "delete-next-character", &P_delete_next_character, 0 }, { "delete-next-word", &P_delete_next_word, 0 }, { "delete-previous-character", &P_delete_previous_character, 0 }, { "delete-previous-word", &P_delete_previous_word, 0 }, { "dollarsign-expand", &P_dollarsign_expand, 0 }, { "end-of-line", &P_end_of_line, 0 }, { "finish-line", &P_finish_line, 0 }, { "follow-symlink", &P_follow_symlink, 0 }, { "forward-character", &P_forward_character, 0 }, { "forward-word", &P_forward_word, 0 }, { "hardpathify", &P_hardpathify, 0 }, { "history-^N", &P_history_control_N, 0 }, { "history-^P", &P_history_control_P, 0 }, { "history-search-forward", &P_history_search_forward, 0 }, { "history-search-reverse", &P_history_search_reverse, 0 }, { "insert-cwd", &P_insert_cwd, 0 }, { "key-binding", &P_key_binding, 0 }, { "key-name", &P_key_name, 0 }, { "kill-entire-line", &P_kill_entire_line, 0 }, { "kill-region", &P_kill_region, 0 }, { "kill-to-end-of-line", &P_kill_to_end_of_line, 0 }, { "literal-next", &P_literal_next, 0 }, { "noop", &P_noop, 0 }, { "number-inc", &P_number_inc, 0 }, { "number-dec", &P_number_dec, 0 }, { "path-next", &P_path_next, 0 }, { "path-prev", &P_path_prev, 0 }, { "query-replace", &P_query_replace, 0 }, { "redraw-line", &P_redraw_line, 0 }, { "ring-yank", &P_ring_yank, 0 }, { "ring-yank-reverse", &P_ring_yank_reverse, 0 }, { "self-insert", &P_self_insert, 1 }, { "set-mark", &P_set_mark, 0 }, { "show-completions", &P_show_completions, 0 }, { "tcsh-history-n", &P_tcsh_history_n, 0 }, { "tcsh-history-p", &P_tcsh_history_p, 0 }, { "transpose-after", &P_transpose_after, 0 }, { "transpose-around", &P_transpose_around, 0 }, { "transpose-before", &P_transpose_before, 0 }, { "transpose-near-after", &P_transpose_near_after, 0 }, { "transpose-near-around", &P_transpose_near_around, 0 }, { "transpose-near-before", &P_transpose_near_before, 0 }, { "yank", &P_yank, 0 } }; static int nPrim = ARRAYSIZE(Prim); static BINDINIT Bind[] = { { "^@", "set-mark" }, { "^A", "beginning-of-line" }, { "^B", "backward-character" }, { "^D", "delete-next-character" }, { "^E", "end-of-line" }, { "^F", "forward-character" }, { "^H", "delete-previous-character" }, { "^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-alter" }, { "ESC-space", "set-mark" }, { "ESC-$", "dollarsign-expand" }, { "ESC-*", "show-completions" }, { "ESC-+", "number-inc" }, { "ESC--", "number-dec" }, { "ESC-.", "insert-cwd" }, { "ESC-6", "case-word-capitalize" }, { "ESC-B", "key-binding" }, { "ESC-D", "insert-cwd" }, { "ESC-H", "hardpathify" }, { "ESC-I", "debug-info-1" }, { "ESC-J", "debug-info-2" }, { "ESC-K", "key-name" }, { "ESC-L", "follow-symlink" }, { "ESC-N", "path-next" }, { "ESC-P", "path-prev" }, { "ESC-Y", "ring-yank-reverse" }, { "ESC-[-A", "history-^P" }, { "ESC-[-B", "history-^N" }, { "ESC-[-C", "forward-character" }, { "ESC-[-D", "backward-character" }, { "ESC-a", "abort" }, { "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-q", "query-replace" }, { "ESC-r", "history-search-reverse" }, { "ESC-s", "history-search-forward" }, { "ESC-u", "case-word-upper" }, { "ESC-y", "ring-yank" }, { "ESC-{"/*}*/, "brace-complete" } }; static int nBind = ARRAYSIZE(Bind); static unsigned char self_insert_init[] = { 0x09, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; static int n_self_insert_init = ARRAYSIZE(self_insert_init); static PRIMINIT Prim_qr[] = { { "all", &P__qr_all, 0 }, { "yes", &P__qr_yes, 0 }, { "no", &P__qr_no, 0 }, { "yes-stop", &P__qr_yes_stop, 0 }, { "no-stop", &P__qr_no_stop, 0 }, { "show", &P__qr_show, 0 } }; static int nPrim_qr = ARRAYSIZE(Prim_qr); static BINDINIT Bind_qr[] = { { "^G", "no-stop" }, { "!", "all" }, { ",", "show" }, { ".", "yes-stop" }, { "N", "no" }, { "n", "no" }, { "space", "yes" } }; static int nBind_qr = ARRAYSIZE(Bind_qr); static const unsigned char *chardisp_init[256] = { "^\100", "^\101", "^\102", "^\103", "^\104", "^\105", "^\106", "^\107", "^\110", "^\111", "^\112", "^\113", "^\114", "^\115", "^\116", "^\117", "^\120", "^\121", "^\122", "^\123", "^\124", "^\125", "^\126", "^\127", "^\130", "^\131", "^\132", "^\133", "^\134", "^\135", "^\136", "^\137", "\040", "\041", "\042", "\043", "\044", "\045", "\046", "\047", "\050", "\051", "\052", "\053", "\054", "\055", "\056", "\057", "\060", "\061", "\062", "\063", "\064", "\065", "\066", "\067", "\070", "\071", "\072", "\073", "\074", "\075", "\076", "\077", "\100", "\101", "\102", "\103", "\104", "\105", "\106", "\107", "\110", "\111", "\112", "\113", "\114", "\115", "\116", "\117", "\120", "\121", "\122", "\123", "\124", "\125", "\126", "\127", "\130", "\131", "\132", "\133", "\134", "\135", "\136", "\137", "\140", "\141", "\142", "\143", "\144", "\145", "\146", "\147", "\150", "\151", "\152", "\153", "\154", "\155", "\156", "\157", "\160", "\161", "\162", "\163", "\164", "\165", "\166", "\167", "\170", "\171", "\172", "\173", "\174", "\175", "\176", "^\077", "^\340", "^\301", "^\302", "^\303", "^\304", "^\305", "^\306", "^\307", "^\310", "^\311", "^\312", "^\313", "^\314", "^\315", "^\316", "^\317", "^\320", "^\321", "^\322", "^\323", "^\324", "^\325", "^\326", "^\327", "^\330", "^\331", "^\332", "^\333", "^\334", "^\335", "^\336", "^\337", "\240", "\241", "\242", "\243", "\244", "\245", "\246", "\247", "\250", "\251", "\252", "\253", "\254", "\255", "\256", "\257", "\260", "\261", "\262", "\263", "\264", "\265", "\266", "\267", "\270", "\271", "\272", "\273", "\274", "\275", "\276", "\277", "\300", "\301", "\302", "\303", "\304", "\305", "\306", "\307", "\310", "\311", "\312", "\313", "\314", "\315", "\316", "\317", "\320", "\321", "\322", "\323", "\324", "\325", "\326", "\327", "\330", "\331", "\332", "\333", "\334", "\335", "\336", "\337", "\340", "\341", "\342", "\343", "\344", "\345", "\346", "\347", "\350", "\351", "\352", "\353", "\354", "\355", "\356", "\357", "\360", "\361", "\362", "\363", "\364", "\365", "\366", "\367", "\370", "\371", "\372", "\373", "\374", "\375", "\376", "\377" };