/* * This program is in public domain; written by Dave G. Conroy. * This file contains the main driving routine, and some keyboard processing * code, for the MicroEMACS screen editor. * * REVISION HISTORY: * * 1.0 Steve Wilhite, 30-Nov-85 * - Removed the old LK201 and VT100 logic. Added code to support the * DEC Rainbow keyboard (which is a LK201 layout) using the the Level * 1 Console In ROM INT. See "rainbow.h" for the function key definitions. * * 2.0 George Jones, 12-Dec-85 * - Ported to Amiga. */ #include #include "ed.h" #if VMS #include #define GOOD (SS$_NORMAL) #endif #ifndef GOOD #define GOOD 0 #endif int currow; /* Working cursor row */ int curcol; /* Working cursor column */ int fillcol; /* Current fill column */ int wordwrap; /* Word wrap flag */ int thisflag; /* Flags, this command */ int lastflag; /* Flags, last command */ int curgoal; /* Goal column */ BUFFER *curbp; /* Current buffer */ WINDOW *curwp; /* Current window */ BUFFER *bheadp; /* BUFFER listhead */ WINDOW *wheadp; /* WINDOW listhead */ BUFFER *blistp; /* Buffer list BUFFER */ BUFFER *prev_bp; /* Previous Buffer */ short kbdm[NKBDM] = {CTLX|')'}; /* Macro */ short *kbdmip; /* Input for above */ short *kbdmop; /* Output for above */ char pat[NPAT]; /* Pattern */ typedef struct { short k_code; /* Key code */ int (*k_fp)(); /* Routine to handle it */ } KEYTAB; extern int ctrlg(); /* Abort out of things */ extern int quit(); /* Quit */ extern int ctlxlp(); /* Begin macro */ extern int ctlxrp(); /* End macro */ extern int ctlxe(); /* Execute macro */ extern int fileread(); /* Get a file, read only */ extern int filevisit(); /* Get a file, read write */ extern int filewrite(); /* Write a file */ extern int filesave(); /* Save current file */ extern int filename(); /* Adjust file name */ extern int getccol(); /* Get current column */ extern int gotobol(); /* Move to start of line */ extern int forwchar(); /* Move forward by characters */ extern int gotoeol(); /* Move to end of line */ extern int backchar(); /* Move backward by characters */ extern int forwline(); /* Move forward by lines */ extern int backline(); /* Move backward by lines */ extern int forwpage(); /* Move forward by pages */ extern int backpage(); /* Move backward by pages */ extern int gotobob(); /* Move to start of buffer */ extern int gotoeob(); /* Move to end of buffer */ extern int setfillcol(); /* Set fill column. */ extern int togglewordwrap(); /* Toggle word wrap */ extern int setmark(); /* Set mark */ extern int swapmark(); /* Swap "." and mark */ extern int forwsearch(); /* Search forward */ extern int backsearch(); /* Search backwards */ extern int qreplace(); /* Query Replace */ extern int showcpos(); /* Show the cursor position */ extern int nextwind(); /* Move to the next window */ extern int prevwind(); /* Move to the previous window */ extern int onlywind(); /* Make current window only one */ extern int splitwind(); /* Split current window */ extern int mvdnwind(); /* Move window down */ extern int mvupwind(); /* Move window up */ extern int enlargewind(); /* Enlarge display window. */ extern int shrinkwind(); /* Shrink window. */ extern int listbuffers(); /* Display list of buffers */ extern int usebuffer(); /* Switch a window to a buffer */ extern int killbuffer(); /* Make a buffer go away. */ extern int reposition(); /* Reposition window */ extern int refresh(); /* Refresh the screen */ extern int twiddle(); /* Twiddle characters */ extern int tab(); /* Insert tab */ extern int newline(); /* Insert CR-LF */ extern int indent(); /* Insert CR-LF, then indent */ extern int openline(); /* Open up a blank line */ extern int deblank(); /* Delete blank lines */ extern int quote(); /* Insert literal */ extern int backword(); /* Backup by words */ extern int forwword(); /* Advance by words */ extern int forwdel(); /* Forward delete */ extern int backdel(); /* Backward delete */ extern int kill(); /* Kill forward */ extern int yank(); /* Yank back from killbuffer. */ extern int upperword(); /* Upper case word. */ extern int lowerword(); /* Lower case word. */ extern int upperregion(); /* Upper case region. */ extern int lowerregion(); /* Lower case region. */ extern int capword(); /* Initial capitalize word. */ extern int delfword(); /* Delete forward word. */ extern int delbword(); /* Delete backward word. */ extern int killregion(); /* Kill region. */ extern int copyregion(); /* Copy region to kill buffer. */ extern int spawncli(); /* Run CLI in a subjob. */ extern int spawn(); /* Run a command in a subjob. */ extern int quickexit(); /* low keystroke style exit. */ extern int parafill(); /* fill paragraph */ /* * Command table. * This table is *roughly* in ASCII order, left to right across the * characters of the command. This expains the funny location of the * control-X commands. */ KEYTAB keytab[] = { CTRL|'@', &setmark, CTRL|'A', &gotobol, CTRL|'B', &backchar, CTRL|'C', &spawncli, /* Run CLI in subjob. */ CTRL|'D', &forwdel, CTRL|'E', &gotoeol, CTRL|'F', &forwchar, CTRL|'G', &ctrlg, CTRL|'H', &backdel, CTRL|'I', &tab, CTRL|'J', &indent, CTRL|'K', &kill, CTRL|'L', &refresh, CTRL|'M', &newline, CTRL|'N', &forwline, CTRL|'O', &openline, CTRL|'P', &backline, CTRL|'Q', "e, /* Often unreachable */ CTRL|'R', &backsearch, CTRL|'S', &forwsearch, /* Often unreachable */ CTRL|'T', &twiddle, CTRL|'V', &forwpage, CTRL|'W', &killregion, CTRL|'Y', &yank, CTRL|'Z', &quickexit, /* quick save and exit */ CTLX|CTRL|'B', &listbuffers, CTLX|CTRL|'C', &quit, /* Hard quit. */ /* CTLX|CTRL|'F', &filename,*/ CTLX|CTRL|'F', &filevisit, CTLX|CTRL|'L', &lowerregion, CTLX|CTRL|'O', &deblank, CTLX|CTRL|'N', &mvdnwind, CTLX|CTRL|'P', &mvupwind, CTLX|CTRL|'R', &fileread, CTLX|CTRL|'S', &filesave, /* Often unreachable */ CTLX|CTRL|'U', &upperregion, CTLX|CTRL|'V', &filevisit, CTLX|CTRL|'W', &filewrite, CTLX|CTRL|'X', &swapmark, CTLX|CTRL|'Z', &shrinkwind, CTLX|'!', &spawn, /* Run 1 command. */ CTLX|'=', &showcpos, CTLX|'(', &ctlxlp, CTLX|')', &ctlxrp, CTLX|'1', &onlywind, CTLX|'2', &splitwind, CTLX|'B', &usebuffer, CTLX|'E', &ctlxe, CTLX|'F', &setfillcol, CTLX|'K', &killbuffer, CTLX|'N', &nextwind, CTLX|'P', &prevwind, CTLX|'T', &togglewordwrap, CTLX|'Z', &enlargewind, META|CTRL|'H', &delbword, META|'!', &reposition, META|'.', &setmark, META|'>', &gotoeob, META|'<', &gotobob, META|'B', &backword, META|'C', &capword, META|'D', &delfword, META|'F', &forwword, META|'L', &lowerword, META|'Q', ¶fill, META|'U', &upperword, META|'V', &backpage, META|'W', ©region, META|'%', &qreplace, META|0x7F, &delbword, 0x7F, &backdel }; #define NKEYTAB (sizeof(keytab)/sizeof(keytab[0])) #if RAINBOW #include "rainbow.h" /* * Mapping table from the LK201 function keys to the internal EMACS character. */ short lk_map[][2] = { Up_Key, CTRL+'P', Down_Key, CTRL+'N', Left_Key, CTRL+'B', Right_Key, CTRL+'F', Shift+Left_Key, META+'B', Shift+Right_Key, META+'F', Control+Left_Key, CTRL+'A', Control+Right_Key, CTRL+'E', Prev_Scr_Key, META+'V', Next_Scr_Key, CTRL+'V', Shift+Up_Key, META+'<', Shift+Down_Key, META+'>', Cancel_Key, CTRL+'G', Find_Key, CTRL+'S', Shift+Find_Key, CTRL+'R', Insert_Key, CTRL+'Y', Options_Key, CTRL+'D', Shift+Options_Key, META+'D', Remove_Key, CTRL+'W', Shift+Remove_Key, META+'W', Select_Key, CTRL+'@', Shift+Select_Key, CTLX+CTRL+'X', Interrupt_Key, CTRL+'U', Keypad_PF2, META+'L', Keypad_PF3, META+'C', Keypad_PF4, META+'U', Shift+Keypad_PF2, CTLX+CTRL+'L', Shift+Keypad_PF4, CTLX+CTRL+'U', Keypad_1, CTLX+'1', Keypad_2, CTLX+'2', Do_Key, CTLX+'E', Keypad_4, CTLX+CTRL+'B', Keypad_5, CTLX+'B', Keypad_6, CTLX+'K', Resume_Key, META+'!', Control+Next_Scr_Key, CTLX+'N', Control+Prev_Scr_Key, CTLX+'P', Control+Up_Key, CTLX+CTRL+'P', Control+Down_Key, CTLX+CTRL+'N', Help_Key, CTLX+'=', Shift+Do_Key, CTLX+'(', Control+Do_Key, CTLX+')', Keypad_0, CTLX+'Z', Shift+Keypad_0, CTLX+CTRL+'Z', Main_Scr_Key, CTRL+'C', Keypad_Enter, CTLX+'!', Exit_Key, CTLX+CTRL+'C', Shift+Exit_Key, CTRL+'Z' }; #define lk_map_size (sizeof(lk_map)/2) #endif #if AMIGA #define CSI 0x9B #endif main(argc, argv) char *argv[]; { register int c; register int f; register int n; char bname[NBUFN]; int mflag; strcpy(bname, "main"); /* Work out the name of */ if (argc > 1) /* the default buffer. */ makename(bname, argv[1]); edinit(bname); /* Buffers, windows. */ vtinit(); /* Displays. */ fillcol = term.t_ncol -2; if (argc > 1) { update(); /* You have to update */ readin(argv[1]); /* in case "[New file]" */ } lastflag = 0; /* Fake last flags. */ loop: update(); /* Fix up the screen */ c = getkey(); if (mpresf != FALSE) { mlerase(); update(); #if AMIGA /* the its action for space interacts badly with mouse * interaction, so we skip it for the amiga. */ #else if (c == ' ') /* ITS EMACS does this */ goto loop; #endif } f = FALSE; n = 1; if (c == (CTRL|'U')) { /* ^U, start argument */ f = TRUE; n = 4; /* with argument of 4 */ mflag = 0; /* that can be discarded. */ mlwrite("Arg: 4"); while (((c=getkey()) >='0' && c<='9') || c==(CTRL|'U') || c=='-') { if (c == (CTRL|'U')) n = n*4; /* * If dash, and start of argument string, set arg. * to -1. Otherwise, insert it. */ else if (c == '-') { if (mflag) break; n = 0; mflag = -1; } /* * If first digit entered, replace previous argument * with digit and set sign. Otherwise, append to arg. */ else { if (!mflag) { n = 0; mflag = 1; } n = 10*n + c - '0'; } mlwrite("Arg: %d", (mflag >=0) ? n : (n ? -n : -1)); } /* * Make arguments preceded by a minus sign negative and change * the special argument "^U -" to an effective "^U -1". */ if (mflag == -1) { if (n == 0) n++; n = -n; } } if (c == (CTRL|'X')) /* ^X is a prefix */ c = CTLX | getctl(); #if AMIGA if( (c&0xFF) == CSI) { int execute; if( kbdmip != NULL) { /* collecting macro */ execute = FALSE; /* don't actually do it */ ctrlg(FALSE, 0); } else execute = TRUE; mouse_handle_event( execute, f, n); goto loop; } #endif if (kbdmip != NULL) { /* Save macro strokes. */ if (c!=(CTLX|')') && kbdmip>&kbdm[NKBDM-6]) { ctrlg(FALSE, 0); goto loop; } if (f != FALSE) { *kbdmip++ = (CTRL|'U'); *kbdmip++ = n; } *kbdmip++ = c; } execute(c, f, n); /* Do it. */ goto loop; } /* * Initialize all of the buffers and windows. The buffer name is passed down * as an argument, because the main routine may have been told to read in a * file by default, and we want the buffer name to be right. */ edinit(bname) char bname[]; { register BUFFER *bp; register WINDOW *wp; bp = bfind(bname, TRUE, 0); /* First buffer */ blistp = bfind("[List]", TRUE, BFTEMP); /* Buffer list buffer */ wp = (WINDOW *) malloc(sizeof(WINDOW)); /* First window */ if (bp==NULL || wp==NULL || blistp==NULL) exit(1); curbp = bp; /* Make this current */ wheadp = wp; curwp = wp; wp->w_wndp = NULL; /* Initialize window */ wp->w_bufp = bp; bp->b_nwnd = 1; /* Displayed. */ wp->w_linep = bp->b_linep; wp->w_dotp = bp->b_linep; wp->w_doto = 0; wp->w_markp = NULL; wp->w_marko = 0; wp->w_toprow = 0; wp->w_ntrows = term.t_nrow-1; /* "-1" for mode line. */ wp->w_force = 0; wp->w_flag = WFMODE|WFHARD; /* Full. */ } /* * This is the general command execution routine. It handles the fake binding * of all the keys to "self-insert". It also clears out the "thisflag" word, * and arranges to move it to the "lastflag", so that the next command can * look at it. Return the status of command. */ execute(c, f, n) { register KEYTAB *ktp; register int status; if ((c>=0x20 && c<=0x7E) /* Self inserting. */ || (c>=0xA0 && c<=0xFE)) { /* * If word wrap is true, the argument is non- * negative, and we are now past fill column, perform word wrap. */ if (wordwrap && n>=0 && getccol(FALSE) > fillcol) wrapword(); if (n <= 0) { /* Fenceposts. */ lastflag = 0; return (n<0 ? FALSE : TRUE); } thisflag = 0; /* For the future. */ status = linsert(n, c); lastflag = thisflag; return (status); } ktp = &keytab[0]; /* Look in key table. */ while (ktp < &keytab[NKEYTAB]) { if (ktp->k_code == c) { thisflag = 0; status = (*ktp->k_fp)(f, n); lastflag = thisflag; return (status); } ++ktp; } lastflag = 0; /* Fake last flags. */ return (FALSE); } /* * Read in a key. * Do the standard keyboard preprocessing. Convert the keys to the internal * character set. */ getkey() { register int c; c = (*term.t_getchar)(); #if RAINBOW if (c & Function_Key) { int i; for (i = 0; i < lk_map_size; i++) if (c == lk_map[i][0]) return lk_map[i][1]; } else if (c == Shift + 015) return CTRL | 'J'; else if (c == Shift + 0x7F) return META | 0x7F; #endif if (c == METACH) { /* Apply M- prefix */ c = getctl(); return (META | c); } if (c>=0x00 && c<=0x1F) /* C0 control -> C- */ c = CTRL | (c+'@'); #if AMIGA else if(( c & 0x80) && ((c&0xFF) != CSI)) { /* Meta-shifted char */ c &= 0x7F; if (c>='a' && c<='z') /* Force to upper */ c -= 0x20; c |= META; } #endif return (c); } /* * Get a key. * Apply control modifications to the read key. */ getctl() { register int c; c = (*term.t_getchar)(); if (c>='a' && c<='z') /* Force to upper */ c -= 0x20; if (c>=0x00 && c<=0x1F) /* C0 control -> C- */ c = CTRL | (c+'@'); return (c); } /* * Fancy quit command, as implemented by Norm. If the current buffer has * changed do a write current buffer and exit emacs, otherwise simply exit. */ quickexit(f, n) { if ((curbp->b_flag&BFCHG) != 0 /* Changed. */ && (curbp->b_flag&BFTEMP) == 0) /* Real. */ filesave(f, n); quit(f, n); /* conditionally quit */ } /* * Quit command. If an argument, always quit. Otherwise confirm if a buffer * has been changed and not written out. Normally bound to "C-X C-C". */ quit(f, n) { register int s; if (f != FALSE /* Argument forces it. */ || anycb() == FALSE /* All buffers clean. */ || (s=mlyesno("Modified buffers exist. Quit")) == TRUE) { /* User says it's OK. */ vttidy(); exit(GOOD); } return (s); } /* * Begin a keyboard macro. * Error if not at the top level in keyboard processing. Set up variables and * return. */ ctlxlp(f, n) { if (kbdmip!=NULL || kbdmop!=NULL) { mlwrite("Not now"); return (FALSE); } mlwrite("[Start macro]"); kbdmip = &kbdm[0]; return (TRUE); } /* * End keyboard macro. Check for the same limit conditions as the above * routine. Set up the variables and return to the caller. */ ctlxrp(f, n) { if (kbdmip == NULL) { mlwrite("Not now"); return (FALSE); } mlwrite("[End macro]"); kbdmip = NULL; return (TRUE); } /* * Execute a macro. * The command argument is the number of times to loop. Quit as soon as a * command gets an error. Return TRUE if all ok, else FALSE. */ ctlxe(f, n) { register int c; register int af; register int an; register int s; if (kbdmip!=NULL || kbdmop!=NULL) { mlwrite("Not now"); return (FALSE); } if (n <= 0) return (TRUE); do { kbdmop = &kbdm[0]; do { af = FALSE; an = 1; if ((c = *kbdmop++) == (CTRL|'U')) { af = TRUE; an = *kbdmop++; c = *kbdmop++; } s = TRUE; } while (c!=(CTLX|')') && (s=execute(c, af, an))==TRUE); kbdmop = NULL; } while (s==TRUE && --n); return (s); } /* * Abort. * Beep the beeper. Kill off any keyboard macro, etc., that is in progress. * Sometimes called as a routine, to do general aborting of stuff. */ ctrlg(f, n) { (*term.t_beep)(); if (kbdmip != NULL) { kbdm[0] = (CTLX|')'); kbdmip = NULL; } return (ABORT); }