/**************************************************************************** pad.c - text editor for linux, uses ncurses, with mouse support Compile with : gcc -g -Wall -o pad pad.c -lncurses -lgpm Without gpm : gcc -g -Wall -o pad pad.c -lncurses -DNO_GPM This is free software in the sense of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. Copyright (C) 1999-2000, paddy.strebel@tiscalinet.ch ****************************************************************************/ char *startmsg = "pad 0.79, (C) 1999-2000, NO WARRANTY"; char *helptext = " Function Keys: ALT-a = META-a = ESC a ALT-h help ALT-x cut ALT-u undo ALT-o open ALT-v paste ALT-a ascii insert ALT-z close ALT-c copy ALT-m match bracket ALT-s save ALT-d delete ALT-i indent sel. ALT-t select ALT-b search bwd ALT-j outdent sel. ALT-y status ALT-f search fwd ALT-k key recording ALT-w window ALT-r search/replace ALT-p key playback ALT-e end ALT-l goto line ALT-n next err ALT-q quit ALT-g goto byte ALT-` execute CTRL-l, r, u, d, p, n = left, right, up, down, prev_page, next_page CTRL-a ascii table, CTRL-` change case select: left mouse button selects char/word/line, hold left and drag or click right mouse button to expand selection later scroll: click left/right/middle on left margin to scroll up/down/to click left/right/middle on bottom line scrolls left/right/to search: click left/right on right margin to search selection bwd/fwd"; char *menutext = " help wtop undo open save quit cut paste copy bwd fwd rpl "; char *optntext = " option (default) description -a, -A (-A) autoindent after newline -b, -B (-B) keep backupfile \"file~\" -c, -C (-c) ignore case when searching -k, -K (-K) black text background -p, -P (-P) activate mouse driver -x, -X (-X) expand window over toolbar -i ... (-i \" \") indent string -m ... (-m padmak) make command -r ... (-r padrun) run command -t ... (-t 0) tab width 0..100 -U/D/M (-U) Unix/Dos/Mac end of line"; char *ascitext = " oct 0000 0020 0040 0060 0100 0120 0140 0160 hex 0x00 0x10 0x20 0x30 0x40 0x50 0x60 0x70 0x0 0 NUL '\\0' 16 DLE 32 48 0 64 @ 80 P 96 ` 112 p 0x1 1 SOH ^a 17 XON 33 ! 49 1 65 A 81 Q 97 a 113 q 0x2 2 STX ^b 18 DC2 34 \" 50 2 66 B 82 R 98 b 114 r 0x3 3 ETX ^c 19 XOF 35 # 51 3 67 C 83 S 99 c 115 s 0x4 4 EOF ^d 20 DC4 36 $ 52 4 68 D 84 T 100 d 116 t 0x5 5 ENQ ^c 21 NAK 37 %% 53 5 69 E 85 U 101 e 117 u 0x6 6 ACK ^d 22 SYN 38 & 54 6 70 F 86 V 102 f 118 v 0x7 7 BEL '\\a' 23 ETB 39 ' 55 7 71 G 87 W 103 g 119 w 0x8 8 BS '\\b' 24 CAN 40 ( 56 8 72 H 88 X 104 h 120 x 0x9 9 HT '\\t' 25 EM 41 ) 57 9 73 I 89 Y 105 i 121 y 0xA 10 LF '\\n' 26 SUB 42 * 58 : 74 J 90 Z 106 j 122 z 0xB 11 VT '\\v' 27 ESC 43 + 59 ; 75 K 91 [ 107 k 123 { 0xC 12 FF '\\f' 28 FS 44 , 60 < 76 L 92 \\ 108 l 124 | 0xD 13 CR '\\r' 29 GS 45 - 61 = 77 M 93 ] 109 m 125 } 0xE 14 SO ^n 30 RS 46 . 63 > 78 N 94 ^ 110 n 126 ~ 0xF 15 SI ^o 31 US 47 / 63 ? 79 O 95 _ 111 o 127 DEL" ; typedef long int LINT; /* 32 bit */ typedef unsigned char BYTE; /* 8 bit */ #include #include #include #include #include #include #include #include #include #include #ifndef NO_GPM #include #define WITH_GPM #endif #define BUT_LEFT 1 #define BUT_MIDDLE 2 #define BUT_RIGHT 4 #define REL_LEFT 8 #define REL_MIDDLE 16 #define REL_RIGHT 32 /* Edited files are is divided into chunks of file positions (BUF.pos) and lengths (BUF.len). The chunks are organized as double pointered ring of BUFs. fil->stb is the first BUF and must not be deleted. Changes and clipboard contents are appended to a temporary file efil. If BUF.pos is negative the position is in efil else the original file. BUF.cre stores the editor step when the chunk is created and BUF.del when it is deleted. This is needed by the undo function. */ #define PAD_DEBUG 1 /* display buffers with CTRL-y */ typedef struct bbb { struct bbb *next; /* pointer to next buffer */ struct bbb *prev; /* pointer to previous buffer */ LINT pos; /* pos in file, -pos if in efil */ LINT len; /* length in file or efil */ int cre; /* creation step */ int del; /* deletion step */ } BUF; typedef struct fff { struct fff *next; /* next FIL structure */ struct fff *disp; /* next FIL on display */ WINDOW *win; /* curses window */ FILE *lst; /* listing file for compiler errors */ BUF *stb; /* start buffer chain */ LINT siz; /* size of edited file */ LINT nln; /* number of lines in edited file */ LINT top; /* start byte of top display line */ LINT sft; /* hoizontal shift in display */ LINT tln; /* top line on display */ LINT ins; /* insert (current byte) */ LINT sel; /* selection from ins to sel */ LINT lin; /* line where cursor is */ LINT col; /* column where cursor is */ LINT ccl; /* current column for up/down movement */ int lins; /* curses window lines */ int cols; /* curses window columns */ int winy; /* curses window y origin */ int winx; /* curses window x origin */ int sta; /* file status: new=1, readonly=2 */ int chg; /* incremented after each change */ int file; /* file handle */ time_t tim; /* modification time */ BYTE name[256]; /* file name */ } FIL; /* options */ int autoind = 1; /* autoindent (leading whitespace) */ int bakfile = 1; /* keep old file as filename~ */ int chkcase = 0; /* case sensitive searches */ int expandw = 1; /* open windows below toolbar */ int blackbg = 1; /* black background */ int mouseon = 1; /* initialize mouse support */ int tabsize = 8; /* tab setting */ int dosmode = 0; /* dos mode flag */ int eol = '\n'; /* end of line character */ BYTE makstr[256]; /* shell command string (save & make) */ BYTE runstr[256]; /* shell command string (run) */ BYTE indstr[128]; /* indent string */ /* global variables */ BYTE fbuf[2048]; /* file read buffer */ BYTE fkeyseq[512]; /* escape sequences of function keys */ BYTE pathstr[256]; /* path when pad started */ BYTE srchstr[256]; /* search string */ BYTE roldstr[256]; /* old search/replace string */ BYTE rnewstr[256]; /* new search/replace string */ BYTE mesgstr[256]; /* message string */ LINT lloc[128]; /* start of each line on screen */ int fkeymap[64]; /* lookup table for function keys */ int dispmap[256]; /* lookup table for char display */ int mbuf[256]; /* keyboard macro buffer */ int kbuf[16]; /* key buffer */ BYTE *progname = ""; /* argv[0] */ WINDOW *msgwin = NULL; /* bottom message line */ FIL *filst = NULL; /* first file opened */ FIL *filbk = NULL; /* backmost file on display */ FIL *filck = NULL; /* file last clicked */ FIL *fil = NULL; /* current file */ BUF *rbuf = NULL; /* read location buffer */ LINT rofs = 0; /* offset in read location buffer */ LINT rloc = 0; /* read location (not a file position) */ LINT epos = 0; /* where inserts are appended */ LINT cpos = 0; /* position of clipboard contents in efil */ LINT clen = 0; /* length of clipboard contents */ LINT clin = 0; /* lines in of clipboard contents */ LINT kins = 0; /* keyboard selection */ LINT ksel = 0; /* keyboard selection */ LINT klin = 0; /* keyboard selection */ LINT kcol = 0; /* keyboard selection */ LINT mins = 0; /* mouse selection */ LINT msel = 0; /* mouse selection */ LINT mlin = 0; /* mouse selection */ LINT mcol = 0; /* mouse selection */ LINT ncol = 0; /* columns of the longest line on display */ int slins = 0; /* LINES of stdscr */ int scols = 0; /* COLS of stdscr */ int efil = -1; /* temporary edit file for inserts */ int fpos = 0; /* current position in fbuf */ int fmax = 0; /* max nr of bytes in fbuf */ int mcnt = 0; /* keyboard macro key count */ int mpos = 0; /* keyboard macro position, -1 recording */ int kcnt = 0; /* menu key buffer count */ int kpos = 0; /* menu key buffer position */ int bcnt = 0; /* files in filebrowser */ int btop = 0; /* top file in filebrowser */ int bsel = 0; /* selected file in filebrowser */ int hascolor = 0; /* use colors if available */ int cursvis = 0; /* cursor stays visible */ int fkeycnt = 0; /* number of special function keys */ int bufcnt = 0; /* number of edit buffers */ int srchlen = 0; /* length of search string */ int roldlen = 0; /* length of old replace string */ int rnewlen = 0; /* length of new replace string */ int mesglen = 0; /* length message string */ int chcod = 0; /* char code under cursor */ int selmode = 0; /* selection mode: char, word, line */ int kslmode = 0; /* keyboard selection mode */ int wchange = 0; /* a window is resized or moved */ int done = 0; /* leave the program when done */ int ignmouse = 0; /* ignore mouse events, pass esc */ int mouseup = 0; /* mousedriver: none, gpm, ncurses */ #ifdef WITH_GPM Gpm_Connect gpmconn; #endif /* prototypes for forward reference */ int getkey(void); /* keyboard input & mouse handling */ /* ncurses routine define_key is used to get control keys working this solution is probably not very portable */ #define KEY_CTRL(x) (x-'`') #define FUNKEY 0700 #define KEY_ALT(x) (FUNKEY+x-'`') void addfkey(BYTE *sequence, int mapkey) { strcpy(&fkeyseq[fkeycnt*8], sequence); #ifdef NCURSES_VERSION define_key(&fkeyseq[fkeycnt*8], FUNKEY+fkeycnt); #endif fkeymap[fkeycnt] = mapkey; fkeycnt++; } int initfkeymap(void) { int i; BYTE seq[4]; for (i = '`'; i <= 'z'; i++) { seq[0] = '\033'; seq[1] = i; seq[2] = '\0'; addfkey(seq, KEY_ALT(i)); } addfkey("\033On", KEY_DC); addfkey("\033Op", KEY_IC); addfkey("\033Ot", KEY_LEFT); addfkey("\033Ov", KEY_RIGHT); addfkey("\033Ow", KEY_DOWN); addfkey("\033Ox", KEY_UP); addfkey("\033OA", KEY_UP); addfkey("\033OB", KEY_DOWN); addfkey("\033OC", KEY_RIGHT); addfkey("\033OD", KEY_LEFT); addfkey("\033OF", KEY_END); addfkey("\033OH", KEY_HOME); addfkey("\033OP", KEY_F(1)); addfkey("\033OQ", KEY_F(2)); addfkey("\033OR", KEY_F(3)); addfkey("\033OS", KEY_F(4)); addfkey("\033[1~", KEY_HOME); addfkey("\033[2~", KEY_IC); addfkey("\033[3~", KEY_DC); addfkey("\033[4~", KEY_END); addfkey("\033[5~", KEY_PPAGE); addfkey("\033[6~", KEY_NPAGE); addfkey("\033[11~", KEY_F(1)); addfkey("\033[12~", KEY_F(2)); addfkey("\033[13~", KEY_F(3)); addfkey("\033[14~", KEY_F(4)); addfkey("\033[15~", KEY_F(5)); addfkey("\033[17~", KEY_F(6)); addfkey("\033[18~", KEY_F(7)); addfkey("\033[19~", KEY_F(8)); addfkey("\033[20~", KEY_F(9)); addfkey("\033[21~", KEY_F(10)); addfkey("\033[[A", KEY_F(1)); addfkey("\033[[B", KEY_F(2)); addfkey("\033[[C", KEY_F(3)); addfkey("\033[[D", KEY_F(4)); addfkey("\033[[E", KEY_F(5)); return (fkeycnt > 64); }; void initdispmap(void) { int i; for (i = 0; i < 32; i++) dispmap[i] = ACS_TTEE; for (i = 32; i < 128; i++) dispmap[i] = i; dispmap[0] = ACS_DEGREE; /* zero */ dispmap[9] = ACS_RTEE; /* tab */ dispmap[10] = ACS_LLCORNER; /* nl */ dispmap[13] = ACS_LRCORNER; /* cr */ dispmap[127] = ACS_BTEE; /* 127 */ for (i = 128; i < 256; i++) dispmap[i] = ACS_PLUS; } void initcolors(void) { if (!hascolor) return; if (blackbg) { init_pair(1, COLOR_WHITE, COLOR_BLACK); /* text */ init_pair(2, COLOR_CYAN, COLOR_BLACK); /* display text */ init_pair(3, COLOR_GREEN, COLOR_BLACK); /* file frame */ init_pair(4, COLOR_CYAN, COLOR_BLACK); /* selection */ init_pair(5, COLOR_WHITE, COLOR_RED); /* messages */ init_pair(6, COLOR_WHITE, COLOR_MAGENTA); /* input */ init_pair(7, COLOR_WHITE, COLOR_BLUE); /* menu */ } else { init_pair(1, COLOR_BLACK, COLOR_WHITE); /* text */ init_pair(2, COLOR_BLUE, COLOR_WHITE); /* display text */ init_pair(3, COLOR_BLUE, COLOR_WHITE); /* file frame */ init_pair(4, COLOR_RED, COLOR_WHITE); /* selection */ init_pair(5, COLOR_CYAN, COLOR_RED); /* messages */ init_pair(6, COLOR_CYAN, COLOR_MAGENTA); /* input */ init_pair(7, COLOR_CYAN, COLOR_BLACK); /* menu */ } wbkgdset(stdscr, COLOR_PAIR(1) | ' '); wbkgdset(msgwin, COLOR_PAIR(5) | ' '); wattrset(msgwin, COLOR_PAIR(5)); } void initcurses(void) { initscr(); getmaxyx(stdscr, slins, scols); keypad(stdscr, TRUE); timeout(-1); nonl(); cbreak(); noecho(); cursvis = (curs_set(0) == ERR); msgwin = newwin(1, scols, slins-1, 0); keypad(msgwin, TRUE); if ((hascolor = has_colors())) start_color(); initcolors(); #ifdef NCURSES_VERSION ESCDELAY = 100; /* faster esc key (see lib_getch.c) */ #endif } void endmouse(void) { #ifdef WITH_GPM if (mouseup == 1) Gpm_Close(); #endif #ifdef NCURSES_MOUSE_VERSION if (mouseup == 2) mousemask(0, NULL); #endif mouseup = 0; } void finish(char *fmt, ...) /* clean up, exit if fmt supplied */ { va_list ap; endmouse(); close(efil); curs_set(1); if (hascolor) wbkgdset(stdscr, COLOR_PAIR(0) | ' '); clear(); refresh(); endwin(); if (fmt[0] == '\0') return; fprintf(stderr, "\n%s: ", progname); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n\n"); exit(1); } void die(char *fmt, ...) { va_list ap; perror("\r\n\n\n\n\n\n\nperror"); finish(""); fprintf(stderr, "\n\nerror "); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n\nsorry to say good bye ...\n"); fprintf(stderr, "examine core dump with: gdb %s core\n", progname); fprintf(stderr, "'up' to see calling proc, 'q' to exit\n\n"); abort(); } void showmsg(void) { int y, x; getyx(msgwin, y, x); wattron(msgwin, A_REVERSE); whline(msgwin, ' ', scols - x); wattroff(msgwin, A_REVERSE); if (cursvis) mvwaddch(msgwin, 0, scols-1, ' '); wrefresh(msgwin); } void flushmsg(void) { int i = 0; if (mesglen > scols - 2) mesglen = scols - 2; wattron(msgwin, A_REVERSE); mvwaddch(msgwin, 0, 0, ' '); while (i < mesglen) waddch(msgwin, dispmap[(int)mesgstr[i++]]); wattroff(msgwin, A_REVERSE); showmsg(); mesglen = 0; } void message(char *fmt, ...) { va_list ap; va_start(ap, fmt); mesglen = vsprintf(mesgstr, fmt, ap); va_end(ap); } void prompt(char *fmt, ...) { va_list ap; char str[256]; int i = 0, j; va_start(ap, fmt); j = vsprintf(str, fmt, ap); va_end(ap); if (j > scols - 2) j = scols - 2; wattron(msgwin, A_REVERSE); mvwaddch(msgwin, 0, 0, ' '); while (i < j) waddch(msgwin, dispmap[(int)str[i++]]); whline(msgwin, ' ', scols - j - 1); wattroff(msgwin, A_REVERSE); wrefresh(msgwin); } void locate(LINT l) /* adjust rbuf, rofs, rloc to l */ { if (l > fil->siz) die("01: locate out of range"); fpos = fmax = 0; rloc = l; rofs = 0; rbuf = fil->stb; if (l == fil->siz) return; l = 0; while (1) { if (!(rbuf->del)) rofs += rbuf->len; if (rofs > rloc) break; l = rofs; rbuf = rbuf->next; } rofs = rloc - l; } int getinc(void) /* read char from edit file and inc loc */ { int n, f; if (rloc >= fil->siz) return EOF; if (fpos >= fmax) { if ((fmax = rbuf->len - rofs) > sizeof(fbuf)) fmax = sizeof(fbuf); f = (rbuf->pos < 0) ? efil : fil->file; if (lseek(f, abs(rbuf->pos)+rofs, SEEK_SET) < 0) die("02: lseek"); fpos = 0; while (fpos < fmax) { if ((n = read(f, &(fbuf[fpos]), fmax-fpos)) < 0) die("03: read"); fpos += n; } fpos = 0; } rloc++; rofs++; if (rofs >= rbuf->len) { do rbuf = rbuf->next; while (rbuf->del); rofs = 0; } fpos++; return fbuf[fpos-1]; } int decget(void) /* dec loc and read char from edit file */ { int n, f; if (rloc == 0) return EOF; rloc--; rofs--; fpos--; if (fpos < 0) { if (rofs < 0) { do rbuf = rbuf->prev; while (rbuf->del); rofs = rbuf->len-1; } if ((fmax = rofs + 1) > sizeof(fbuf)) fmax = sizeof(fbuf); f = (rbuf->pos < 0) ? efil : fil->file; if (lseek(f, abs(rbuf->pos)+rofs+1-fmax, SEEK_SET) < 0) die("04: lseek"); fpos = 0; while (fpos < fmax) { if ((n = read(f, &(fbuf[fpos]), fmax-fpos)) < 0) die("05: read"); fpos += n; } fpos--; } return (fbuf[fpos]); } void showmenu(void) { char str[16]; clear(); if (hascolor) wattrset(stdscr, COLOR_PAIR(7)); wattron(stdscr, A_REVERSE); if (autoind) strcpy(str, " A"); else strcpy(str, " a"); if (chkcase) strcat(str, " C"); else strcat(str, " c"); if (blackbg) strcat(str, " K "); else strcat(str, " k "); mvwhline(stdscr, 0, 0, ' ', scols); mvwprintw(stdscr, 0, 0, menutext); mvwprintw(stdscr, 0, scols-7, str); wattroff(stdscr, A_REVERSE); } void showscreen(char *title, char *text) { if (hascolor) wattrset(stdscr, COLOR_PAIR(2)); clear(); mvwprintw(stdscr, 1, 1, text); box(stdscr, 0, 0); mvwprintw(stdscr, 0, 1, " %s ", title); wattroff(stdscr, A_REVERSE); if (cursvis) mvwaddch(stdscr, slins-1, scols-1, ACS_LRCORNER); wrefresh(stdscr); } void paint(void) { char str[80]; FIL *f; int l = 0, ch = 0, d = 0, i = 0; int atxt = 0, asel = A_REVERSE; LINT c = 0; if (filst == NULL) { showscreen("pad help (no files open)", helptext); if (mesglen) flushmsg(); return; } if (hascolor) { wattrset(fil->win, COLOR_PAIR(1)); wbkgdset(fil->win, COLOR_PAIR(1) | ' '); asel |= COLOR_PAIR(4); } if (fil->disp != NULL) { if (hascolor) wattrset(fil->win, COLOR_PAIR(3)); asel = A_REVERSE; } if (ncol > 10000) prompt("busy (long line)"); locate(fil->top); wmove(fil->win, 1, 1); lloc[l] = rloc; ncol = 0; if (rloc >= fil->ins && rloc <= fil->sel) atxt = asel; while (1) { ch = getinc(); if (ch == EOF) { if (c >= ncol) ncol = c + 1; if (fil->sel == fil->siz) chcod = 0; if (c < fil->sft + fil->cols - 2) { if (fil->sel == fil->siz) atxt = asel; else atxt = 0; if (c >= fil->sft) waddch(fil->win, ACS_DIAMOND | atxt); wclrtoeol(fil->win); } while (l <= fil->lins-2) { l++; lloc[l] = rloc; wmove(fil->win, l + 1, 1); wclrtoeol(fil->win); } break; } i = 1; if (ch == '\t' && tabsize) { i = tabsize - (c % tabsize); d = ' '; } else if (ch == eol) { if (!dosmode) { d = ' '; } else if (d == dispmap[(int)'\r']) { /* crlf? */ d = ' '; wmove(fil->win, l + 1, c - fil->sft); c--; if (rloc-1 == fil->ins) { /* select cr and lf */ fil->ins--; atxt = asel; chcod = eol; } else if (rloc-2 == fil->sel) { fil->sel++; } } else d = dispmap[(int)'\n']; } else { d = dispmap[ch]; } if (rloc-1 == fil->ins) { atxt = asel; chcod = ch; } else if (rloc-2 == fil->sel) { atxt = 0; } while (i--) { if (c >= fil->sft && c < fil->sft + fil->cols - 2) { waddch(fil->win, d | atxt); } else d = 0; c++; } if (ch == eol) { if (c > ncol) ncol = c; if (c-fil->sft < fil->cols-2) wclrtoeol(fil->win); c = 0; l++; if (l > fil->lins-2) break; lloc[l] = rloc; wmove(fil->win, l + 1, 1); } } if (hascolor) wattrset(fil->win, COLOR_PAIR(3)); if (fil->disp == NULL) wattron(fil->win, A_REVERSE); mvwvline(fil->win, 1, 0, ACS_VLINE, fil->lins-2); i = fil->lins - 2; l = fil->nln + 1; mvwvline(fil->win, (fil->tln*i)/l+1, 0, ACS_CKBOARD, (i*i)/l+1); mvwhline(fil->win, fil->lins-1, 1, ACS_HLINE, fil->cols-2); i = fil->cols - 2; l = ncol + 1; mvwhline(fil->win, fil->lins-1, (fil->sft*i)/l+1, ACS_CKBOARD, (i*i)/l+1); mvwvline(fil->win, 1, fil->cols-1, ACS_VLINE, fil->lins-2); mvwhline(fil->win, 0, 1, ACS_HLINE, fil->cols-2); i = sprintf(str, " L:%ld C:%ld %ld:%03o=%03d=%02x ", fil->lin+1, fil->col+1, fil->ins, chcod, chcod, chcod); mvwprintw(fil->win, 0, fil->cols-1-i, str); mvwaddch(fil->win, 0, 1, ' '); for (i = 0; fil->name[i]; i++) waddch(fil->win, dispmap[fil->name[i]]); waddch(fil->win, ' '); if (fil->sta == 1) wprintw(fil->win, "(new) "); if (fil->sta == 2) wprintw(fil->win, "(read only) "); mvwaddch(fil->win, 0, 0, ACS_ULCORNER); mvwaddch(fil->win, 0, fil->cols-1, ACS_URCORNER); mvwaddch(fil->win, fil->lins-1, 0, ACS_LLCORNER); mvwaddch(fil->win, fil->lins-1, fil->cols-1, ACS_LRCORNER); wattroff(fil->win, A_REVERSE); if (cursvis) mvwaddch(fil->win, fil->lins-1, fil->cols-1, ACS_LRCORNER); touchwin(stdscr); wnoutrefresh(stdscr); for (f = filbk; f != NULL; f = f->disp) { touchwin(f->win); wnoutrefresh(f->win); } if (mesglen) flushmsg(); else doupdate(); } void putkey(int k) { kbuf[kcnt = (kcnt + 1) % 16] = k; } int scanch(int *ch, char *expect) { int i, a, key; ignmouse = 1; if (hascolor) a = COLOR_PAIR(6); else a = 0; waddch(msgwin, expect[0] | a | A_REVERSE); showmsg(); while (1) { key = getkey(); if (key == '\033' || key == KEY_CANCEL) { message("cancelled"); ignmouse--; return -1; } if (key == '\015') { *ch = expect[0]; ignmouse--; return 0; } *ch = tolower(key); for (i = 0; expect[i] > '\0'; i++) { if (*ch == expect[i]) { ignmouse--; return 0; } } } } int scanlong(LINT *l) { char str[20]; int i = 0, j, a, x, key, ch; ignmouse = 1; if (hascolor) a = COLOR_PAIR(6); else a = 0; getyx(msgwin, j, x); while (1) { wmove(msgwin, 0, x); str[i] = '\0'; for (j = 0; j <= i; j++) { ch = dispmap[(int)str[j]]; if (j == i) ch = ' '; else ch |= A_REVERSE; waddch(msgwin, ch | a); } showmsg(); key = getkey(); if (key == '\033' || key == KEY_CANCEL) { message("cancelled"); ignmouse--; return -1; } if (key == '\015') break; if (key >= ' ' && key < 127 && i < sizeof(str)) { str[i] = key; i++; } if ((key == KEY_BACKSPACE || key == 010 || key == 0177) && i) i--; } if (sscanf(str, "%li", l) != 1) *l = -1; ignmouse--; return 0; } int scanstr(BYTE *str, int *len, int maxlen) { int i, j, a, x, key, ch, first = 1; LINT l; ignmouse = 1; if (hascolor) a = COLOR_PAIR(6); else a = 0; i = *len; getyx(msgwin, j, x); while (1) { wmove(msgwin, 0, x); str[*len] = '\0'; for (j = 0; j <= *len; j++) { if (j == *len) ch = ' '; else ch = dispmap[(int)str[j]]; if (str[j] == '\n') ch = ACS_LLCORNER; if (j != i) ch |= A_REVERSE; waddch(msgwin, ch | a); } showmsg(); key = getkey(); if (key == '\033' || key == KEY_CANCEL) { message("cancelled"); ignmouse--; return -1; } if (key == '\015') { ignmouse--; return 0; } if (key >= ' ' && key < 127 && *len < maxlen) { if (first) i = *len = 0; if (*len > i) for (j = *len; j > i; j--) str[j] = str[j-1]; str[i] = key; i++; (*len)++; } switch (key) { case KEY_BACKSPACE: case 0177: case 010: if (!i) break; for (j = i; j < *len; j++) str[j-1] = str[j]; i--; (*len)--; break; case KEY_LEFT: case KEY_CTRL('l'): if (i) i--; break; case KEY_RIGHT: case KEY_CTRL('r'): if (i < *len) i++; break; case KEY_DC: i = *len = 0; break; case KEY_ALT('a'): wattron(msgwin, A_REVERSE); mvwprintw(msgwin, 0, x, "ASCII code (024=20=0x14): "); wattroff(msgwin, A_REVERSE); if (scanlong(&l)) break; if (l < 0 || l > 255 || *len >= maxlen) break; if (first) i = *len = 0; if (*len > i) for (j = *len; j > i; j--) str[j] = str[j-1]; str[i] = l; i++; (*len)++; break; default: break; } if (bcnt) { switch (key) { case KEY_ALT('o'): case KEY_F(2): case KEY_OPEN: ignmouse--; return -1; case KEY_UP: case KEY_CTRL('u'): *len = -1; return 0; case KEY_DOWN: case KEY_CTRL('d'): *len = -2; return 0; case KEY_PPAGE: case KEY_PREVIOUS: case KEY_CTRL('p'): case KEY_A3: *len = -3; return 0; case KEY_NPAGE: case KEY_NEXT: case KEY_CTRL('n'): case KEY_C3: *len = -4; return 0; case KEY_HOME: *len = -5; return 0; case KEY_ALT('d'): *len = -6; return 0; /* case KEY_ALT('r'): *len = -7; return 0; case KEY_ALT('c'): *len = -8; return 0; */ case KEY_ALT('h'): case KEY_F(1): case KEY_HELP: case KEY_CTRL('a'): *len = -99; putkey(key); return 0; default: break; } } first = 0; } } /* BUF *insertbuf: into buf at ofs, initialize pos and len, if 0 < ofs < buf->len buf is splitted and if len == 0 the adress of the second buffer is returned instead of inserting a buffer, else if ofs == 0 the new buffer is inserted before buf else if ofs >= len the new buffer is inserted after buf the address of the inserted buffer is returned */ BUF *insertbuf(BUF *buf, LINT ofs, LINT pos, LINT len) { BUF *b; if (ofs > 0 && ofs < buf->len) { /* split buffer */ b = malloc(sizeof(BUF)); if (b == NULL) die("06: allocate edit buf"); bufcnt++; b->prev = buf; b->next = buf->next; buf->next->prev = b; buf->next = b; if (buf->pos < 0) b->pos = buf->pos - ofs; else b->pos = buf->pos + ofs; b->len = buf->len - ofs; buf->len = ofs; b->cre = buf->cre; b->del = 0; if (!len) return b; /* ok, nothing to insert */ } b = malloc(sizeof(BUF)); if (b == NULL) die("07: allocate edit buf"); bufcnt++; if (ofs) { /* append after buf */ b->next = buf->next; b->prev = buf; buf->next->prev = b; buf->next = b; } else { /* insert before buf */ b->next = buf; b->prev = buf->prev; buf->prev->next = b; buf->prev = b; } b->pos = pos; b->len = len; if (fil == NULL) b->cre = 0; else b->cre = fil->chg; b->del = 0; return b; } void deletebuf(BUF *buf) { if (buf == fil->stb) die("08: delete stb"); buf->prev->next = buf->next; buf->next->prev = buf->prev; free(buf); bufcnt--; } void popdisp(FIL *fil) /* remove fil from display stack */ { FIL *x; if (fil->disp == fil) return; if (filbk == fil) { filbk = fil->disp; } else { x = filbk; while (x->disp != fil) x = x->disp; x->disp = fil->disp; } } void putdisp(FIL *fil, FIL *f) /* put fil under f on display stack */ { FIL *x; if (filbk == NULL || filbk == f) { f = filbk; filbk = fil; fil->disp = f; } else { x = filbk; while (x->disp != NULL && x->disp != f) x = x->disp; f = x->disp; x->disp = fil; fil->disp = f; } } void topdisp(FIL *f) { if (fil == f) return; popdisp(f); putdisp(f, NULL); paint(); fil = f; paint(); } void freefil(int reopen) { FIL *x; struct stat st; while (fil->stb->next != fil->stb) deletebuf(fil->stb->next); if (fil->lst != NULL) { fclose(fil->lst); fil->lst = NULL; } if (reopen) { fil->file = open(fil->name, O_RDONLY); if (fil->file < 0) die("09: re-open file %s", fil->name); if (access(fil->name, W_OK)) fil->sta = 2; else fil->sta = 0; if (fstat(fil->file, &st)) die("10: fstat %s", fil->name); fil->tim = st.st_mtime; fil->chg = 0; insertbuf(fil->stb, 1, 0, fil->siz); return; } popdisp(fil); if (fil == filst) { x = filst = fil->next; } else { x = filst; while (x->next != fil) x = x->next; x->next = fil->next; } free(fil->stb); delwin(fil->win); free(fil); fil = x; } LINT scrolldown(LINT cnt) /* return: lines not scrolled */ { locate(fil->top); while (fil->tln < (fil->nln - (fil->lins-2)/2) && cnt) { if (getinc() == eol) { cnt--; fil->tln++; } } fil->top = rloc; return cnt; } LINT scrollup(LINT cnt) /* return: lines not scrolled */ { int ch; locate(fil->top); while (fil->tln && cnt) { if (decget() == eol) { fil->tln--; cnt--; } } do (ch = decget()); while (ch != EOF && ch != eol); if (ch == EOF) fil->top = 0; else fil->top = rloc+1; return cnt; } void scrolltoline(LINT line) { prompt("scrolling"); if (line <= fil->tln/2) { /* for better speed */ fil->top = fil->tln = 0; } else if (line > fil->tln+(fil->nln-fil->tln)/2) { fil->top = fil->siz; fil->tln = fil->nln; } if (fil->tln < line) scrolldown(line - fil->tln); else scrollup(fil->tln - line); } void scrolltocol(LINT col) { fil->sft = col; if (fil->sft > ncol - (fil->cols/2)) fil->sft = ncol - (fil->cols/2); if (fil->sft < 0) fil->sft = 0; } int viewsel(void) /* if ins is out of screen scroll to view it */ { LINT l = 0, i = 0; if (fil->col < fil->sft || fil->col >= fil->sft + fil->cols-2) { scrolltocol(fil->col - fil->cols/2); i += 1; } if (fil->lin < fil->tln || fil->lin >= fil->tln + fil->lins-2) { l = fil->lin - (fil->lins-2)/2; if (l < 0) l = 0; scrolltoline(l); i += 2; } return i; } void winhome(void) /* if ins is out of screen move it into */ { if (fil->col < fil->sft || fil->col >= fil->sft + fil->cols-2 || fil->lin < fil->tln || fil->lin >= fil->tln + fil->lins-2) { fil->sft = fil->ccl = fil->col = 0; fil->ins = fil->top; fil->lin = fil->tln; } } /* getofs: returns byte offset of x within line starting at lloc adjusts fil->col to the byte pointed by x */ LINT getofs(LINT lloc, LINT x) { LINT ofs = 0, len = 0, col = 0; int ch, c = 0; locate(lloc); while (1) { ch = getinc(); col = len; if (ch == '\t' && tabsize) len += tabsize - (len % tabsize); else len++; if (len > x || ch == eol || ch == EOF) { if (dosmode && c == '\r' && ch == '\n') col--; fil->col = col; return ofs; } c = ch; ofs++; } } LINT getcol(LINT loc) { LINT col = 0; int ch; locate(loc); while (rloc) if (decget() == eol) break; if (rloc) locate(rloc+1); else locate(0); while (rloc < loc) { ch = getinc(); if (ch == '\t' && tabsize) col += tabsize - (col % tabsize); else col++; } if (dosmode && ch == '\r' && getinc() == '\n') col--; return col; } void selectline(void) { int ch; if (fil->ins <= fil->siz) { locate(fil->ins); do ch = getinc(); while (ch != eol && ch != EOF); if (ch == EOF) fil->sel = fil->siz; else fil->sel = rloc - 1; locate(fil->ins); do ch = decget(); while (ch != eol && ch != EOF); if (ch == EOF) fil->ins = 0; else fil->ins = rloc + 1; } fil->col = 0; } void selectword(void) { int ch; locate(fil->ins); ch = getinc(); if (isalnum(ch) || ch == '_') { do ch = getinc(); while (isalnum(ch) || ch == '_'); if (ch == EOF) fil->sel = fil->siz - 1; else fil->sel = rloc - 2; locate(fil->ins); do ch = decget(); while (isalnum(ch) || ch == '_'); if (ch == EOF) fil->ins = 0; else fil->ins = rloc + 1; } fil->col = getcol(fil->ins); } void expandsel(void) { fil->sel = fil->ins; if (selmode == 1) selectword(); else if (selmode == 2) selectline(); } void readfil(FIL *fil) { int ch; LINT x = 1, z; fil->lin = fil->nln = fil->top = fil->tln = 0; z = (fil->siz/100)+1; locate(0); while ((ch = getinc()) != EOF) { if (ch == eol) { if (rloc <= fil->ins) fil->lin++; fil->nln++; } if (rloc == x) { prompt("reading %s %ld%%", fil->name, x/z); x += z; } } fil->col = getcol(fil->ins); } int openfil(BYTE *filename) { BYTE curpath[256]; struct stat st; int i, j; FIL *x; if (!filename[0]) { message("cannot open \"%s\"", filename); return -1; } if (stat(filename, &st)) st.st_mode = 0; if (S_ISDIR(st.st_mode)) { message("cannot edit directory \"%s\"", filename); return -1; } if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { message("cannot edit device \"%s\"", filename); return -1; } if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) { message("cannot edit special file \"%s\"", filename); return -1; } if (getcwd(curpath, sizeof(curpath)) == NULL) die("11: getcwd"); i = strlen(filename); j = strlen(curpath); fil = malloc(sizeof(FIL)); if (fil == NULL) die("12: allocate filebuf"); fil->next = NULL; if (filst == NULL) { filst = fil; } else { x = filst; while (x->next != NULL) x = x->next; x->next = fil; } fil->stb = malloc(sizeof(BUF)); if (fil->stb == NULL) die("13: allocate filebuf"); fil->stb->prev = fil->stb; fil->stb->next = fil->stb; fil->disp = fil; fil->stb->pos = 0; fil->stb->len = 0; fil->stb->cre = -1; fil->stb->del = 0; if (i + j + 2 > sizeof(fil->name)) die("14: pathsize"); if (filename[0] == '/') { strcpy(fil->name, filename); } else { strcpy(fil->name, curpath); if (curpath[j-1] != '/') strcat(fil->name, "/"); strcat(fil->name, filename); } fil->siz = 0; fil->nln = 0; fil->top = 0; fil->sft = 0; fil->tln = 0; fil->ins = 0; fil->sel = 0; fil->lin = 0; fil->col = 0; fil->ccl = 0; fil->sta = 0; fil->chg = 0; fil->tim = 0; fil->lst = NULL; if (expandw) fil->winy = 0; else fil->winy = 1; fil->winx = 0; fil->lins = slins - fil->winy; fil->cols = scols - fil->winx; fil->win = newwin(fil->lins, fil->cols, fil->winy, fil->winx); if (fil->win == NULL) die("15: creating window"); if (access(fil->name, F_OK)) { fil->sta = 1; fil->file = open(fil->name, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fil->file < 0) { message("cannot create file %s", fil->name); freefil(0); return -1; } message("new file %s opened", fil->name); return 0; } fil->file = open(fil->name, O_RDONLY); if (fil->file < 0) { message("cannot read file %s", fil->name); freefil(0); return -1; } if (fstat(fil->file, &st)) die("16: fstat %s", fil->name); fil->tim = st.st_mtime; if (access(fil->name, W_OK)) fil->sta = 2; if (lseek(fil->file, 0, SEEK_END) < 0) die("17: lseek"); if ((fil->siz = lseek(fil->file, 0, SEEK_CUR)) < 0) die("18: get size"); if (fil->siz > 0) insertbuf(fil->stb, 1, 0, fil->siz); readfil(fil); if (fil->sta == 2) message("read-only file %s opened", fil->name); else message("file %s opened", fil->name); return 0; } void resize(int lins, int cols, int winy, int winx) { WINDOW *win; if (lins < 3 || cols < 10) { message("window too small"); return; } win = newwin(lins, cols, winy, winx); if (win == NULL) die("19: creating new window"); delwin(fil->win); fil->win = win; fil->lins = lins; fil->cols = cols; fil->winy = winy; fil->winx = winx; } void rereadall(void) { FIL *x; for (x = filst; x != NULL; x = x->next) { readfil(x); viewsel(); } } void efilwrite(BYTE *str, int len) { int i = 0, n = 0; if (lseek(efil, epos, SEEK_SET) < 0) die("20: lseek efil"); while (i < len) { if ((n = write(efil, str+i, len-i)) < 0) die("21: write efil"); i += n; } epos += len; } void gotobyte(LINT x) { if (x < 0) x = 0; else if (x > fil->siz) x = fil->siz; if (x <= fil->ins/2) { /* for better speed */ fil->ins = fil->lin = 0; } else if (x > fil->ins+(fil->siz-fil->ins)/2) { fil->ins = fil->siz; fil->lin = fil->nln; } locate(fil->ins); while (rloc < x) if (getinc() == eol) fil->lin++; while (rloc > x) if (decget() == eol) fil->lin--; fil->ins = fil->sel = x; fil->ccl = fil->col = getcol(fil->ins); viewsel(); } void gotoline(LINT x) { if (x < 1) x = 1; else if (x > fil->nln+1) x = fil->nln+1; x--; if (x <= fil->lin/2) { /* for better speed */ fil->ins = fil->lin = 0; } else if (x > fil->lin+(fil->nln-fil->lin)/2) { fil->ins = fil->siz; fil->lin = fil->nln; } locate(fil->ins); while (fil->lin < x) if (getinc() == eol) fil->lin++; while (fil->lin > x) if (decget() == eol) fil->lin--; x = rloc; fil->ins = fil->sel = x - getcol(rloc); fil->ccl = fil->col = 0; viewsel(); } void copysel() { BYTE buf[1024]; int ch, len = 0, done = 0; if (fil->ins >= fil->siz) return; locate(fil->ins); cpos = epos; clen = clin = 0; while (!done) { buf[len] = ch = getinc(); if (ch == EOF) { done = 1; } else { if (ch == eol) clin++; len++; } if (rloc > fil->sel) done = 1; if (len >= sizeof(buf) || done) { efilwrite(buf, len); clen += len; len = 0; } } efilwrite("!", 1); /* spacer for undo */ } void delsel() { BUF *a, *b; LINT ofs; if (fil->ins >= fil->siz) return; if (fil->sel >= fil->siz) fil->sel = fil->siz-1; locate(fil->ins); a = b = rbuf; ofs = rofs; while (rloc <= fil->sel) { if (getinc() == eol) fil->nln--; if (rbuf != b) { if (ofs) b = insertbuf(b, ofs, 0, 0); b->del = fil->chg; b = rbuf; ofs = 0; } } if (rofs) { if (b == a && ofs) { b = insertbuf(b, ofs, 0, 0); rofs -= ofs; } insertbuf(b, rofs, 0, 0); b->del = fil->chg; } fil->siz -= fil->sel - fil->ins + 1; fil->sel = fil->ins; } void paste(void) { locate(fil->ins); insertbuf(rbuf, rofs, 0 - cpos, clen); fil->ins += clen; fil->sel = fil->ins; fil->siz += clen; if (clin) { fil->nln += clin; fil->lin += clin; } fil->ccl = fil->col = getcol(fil->ins); viewsel(); } void inschar(BYTE ch) { BUF *a; locate(fil->ins); a = rbuf->prev; /* inschar at end of last insert? */ if (rofs == 0 && a->pos < 0 && a->len - a->pos == epos) { a->len++; } else { fil->chg++; insertbuf(rbuf, rofs, 0 - epos, 1); } efilwrite(&ch, 1); fil->sel = fil->ins += 1; fil->siz++; if (ch == eol) { efilwrite("!", 1); /* spacer for undo */ fil->nln++; fil->lin++; fil->col = 0; } else if (ch == '\t' && tabsize) { fil->col += (tabsize - (fil->col % tabsize)); } else { fil->col++; } fil->ccl = fil->col; viewsel(); } int delchar(void) { BUF *b, *n; int ch; LINT x; fil->sel = fil->ins; locate(fil->ins); if ((ch = decget()) == EOF) return ch; fil->sel = fil->ins = rloc; x = epos; if (ch == eol) x--; /* ignore undo spacer */ /* delete char at end of last insert? */ if (rofs == rbuf->len - 1 && rbuf->pos < 0 && rbuf->len - rbuf->pos == x) { epos = x - 1; if (rofs) { rbuf->len--; } else { b = rbuf->prev; n = rbuf->next; deletebuf(rbuf); /* concat buffers if possible */ if (b->cre == n->cre && !(b->del) && !(n->del) && (b != n)) { b->len += n->len; deletebuf(n); } fil->chg--; } if (ch == eol) fil->nln--; fil->siz--; } else { fil->chg++; delsel(); } if (ch == eol) fil->lin--; if (ch == eol || ch == '\t') fil->col = getcol(fil->ins); else fil->col--; fil->ccl = fil->col; viewsel(); return ch; } int seltostr(BYTE *str, int max) { int ch, len = 0; locate(fil->ins); while ((ch = getinc()) != EOF) { if (rloc > fil->sel+1) break; if (len >= max - 1) break; str[len++] = ch; } str[len] = '\0'; return len; } int compare(BYTE a, BYTE b) { if (a == b) return 1; return (!chkcase && (tolower(a) == tolower(b))); } int searchdown(BYTE *sstr, int slen, int skip) { int ch = 0, i = 0, j = 0, k = 0; LINT l = 0; if (!slen) { message("no search string defined."); return 0; } prompt("searching ..."); locate(fil->ins); while (skip--) if (getinc() == eol) l++; while ((ch = getinc()) != EOF) { if (ch == eol) l++; if (compare(sstr[i], ch)) { i++; if (i == slen) { /* found */ fil->ins = rloc - i; fil->sel = rloc - 1; while (rloc > fil->ins) if (decget() == eol) l--; fil->lin += l; fil->ccl = fil->col = getcol(fil->ins); viewsel(); return 1; } } else { j = 1; k = i; i = 0; while (j < k) { if (compare(sstr[i], sstr[j])) { i++; j++; } else { j -= i - 1; i = 0; } } if (compare(sstr[i], ch)) i++; else i = 0; } } message("not found: "); for (i = 0; i < srchlen && mesglen < sizeof(mesgstr); i++) { mesgstr[mesglen++] = srchstr[i]; } return 0; } int searchup(void) { int ch = 0, i = 0, j = 0, k = 0; LINT l = 0; if (!srchlen) { message("no search string defined."); return 0; } prompt("searching up ..."); i = srchlen - 1; if (fil->ins+i < fil->siz) locate(fil->ins+i); else locate(fil->sel); while ((ch = decget()) != EOF) { if (ch == eol && rloc < fil->ins) l++; if (compare(srchstr[i], ch)) { i--; if (i < 0) { /* found */ fil->ins = rloc; fil->sel = rloc + srchlen - 1; fil->lin -= l; fil->ccl = fil->col = getcol(fil->ins); viewsel(); return 1; } } else { j = srchlen - 2; k = i; i = srchlen - 1; while (j > k) { if (compare(srchstr[i], srchstr[j])) { i--; j--; } else { j += srchlen - i - 2; i = srchlen - 1; } } if (compare(srchstr[i], ch)) i--; else i = srchlen - 1; } } message("not found: "); for (i = 0; i < srchlen && mesglen < sizeof(mesgstr); i++) { mesgstr[mesglen++] = srchstr[i]; } return 0; } void searchreplace(void) { struct {LINT top, tln, sft, ins, sel, lin, col;} vsave; int ch, i = 0, n = 0, wis = 0, ask = 0, skip = 0, first = 1; LINT l = 0; viewsel(); paint(); prompt("replace what? : "); if (scanstr(roldstr, &roldlen, sizeof(roldstr))) return; if (!roldlen) { message("no string to replace"); return; } wattron(msgwin, A_REVERSE); mvwprintw(msgwin, 0, 0, " replace '"); for (i = 0; i < roldlen; i++) { waddch(msgwin, dispmap[(int)roldstr[i]]); if (i > 16 && roldlen > 20) { wprintw(msgwin, " .."); break; } } wprintw(msgwin, "' with? : "); wattroff(msgwin, A_REVERSE); if (scanstr(rnewstr, &rnewlen, sizeof(rnewstr))) return; efilwrite(rnewstr, rnewlen); for (i = 0; i < rnewlen; i++) if (rnewstr[i] == eol) l++; prompt("to end of file [e] or within selection? [s]: "); if (scanch(&ch, "es")) return; if (ch == 's') wis = 1; prompt("verify each change? [y/n]: "); if (scanch(&ch, "yn")) return; if (ch == 'y') ask = 1; vsave.top = fil->top; /* save view */ vsave.tln = fil->tln; vsave.sft = fil->sft; vsave.ins = fil->ins; vsave.sel = fil->sel; vsave.lin = fil->lin; vsave.col = fil->col; while (searchdown(roldstr, roldlen, skip)) { skip = 1; if (wis && fil->sel > vsave.sel) break; if (ask) { paint(); prompt("replace? [y/n]: "); if (scanch(&ch, "yn")) break; } if (!ask || ch == 'y') { if (ask || first) fil->chg++; first = 0; skip = rnewlen; delsel(); if (rnewlen) { locate(fil->ins); insertbuf(rbuf, rofs, rnewlen - epos, rnewlen); fil->siz += rnewlen; fil->nln += l; } n++; if (fil->sel <= vsave.sel) vsave.sel += (rnewlen-roldlen); } } fil->top = vsave.top; /* restore view */ fil->tln = vsave.tln; fil->sft = vsave.sft; fil->ins = vsave.ins; fil->sel = vsave.sel; fil->lin = vsave.lin; fil->ccl = fil->col = vsave.col; efilwrite("!", 1); /* spacer to typed text */ message("%d strings replaced", n); } void indent(void) { int c = 0, j = 0, ch = 0; BUF *a; if (!(j = strlen(indstr))) return; fil->chg++; efilwrite(indstr, j); locate(fil->ins); c = fil->col; do { if (ch == EOF) break; if (ch == eol) c = 0; if (c >= fil->col) { a = insertbuf(rbuf, rofs, j - epos, j); fil->siz += j; fil->sel += j; if (rloc < fil->top) fil->top += j; locate(rloc+j); c = -1; } ch = getinc(); if (c >= 0) { if (ch == '\t' && tabsize) c += tabsize - (c % tabsize); else c++; } } while (rloc <= fil->sel); efilwrite("!", 1); /* spacer to typed text */ } void outdent(void) { int ch = 0, c = 0, i = 0, j = 0; LINT sel, ins; if (!(j = strlen(indstr))) return; fil->chg++; locate(fil->ins); ins = fil->ins; sel = fil->sel; c = fil->col; do { if (ch == EOF) break; if (ch == eol) c = 0; if (c >= fil->col) { i = 0; fil->ins = rloc; while (i < j) { ch = getinc(); if (ch == eol || ch == EOF) break; if (rloc <= fil->top) fil->top--; if (sel > ins) sel--; i++; } if (i) { fil->sel = fil->ins + i - 1; delsel(); } locate(fil->ins); c = -1; } ch = getinc(); if (c >= 0) { if (ch == '\t' && tabsize) c += tabsize - (c % tabsize); else c++; } } while (rloc <= sel); fil->ins = ins; fil->sel = sel; } void matchbracket(void) { int br1, br2, back, ch; LINT level = 0, l = 0; locate(fil->ins); br1 = getinc(); switch (br1) { case '{': br2 = '}'; back = 0; break; case '}': br2 = '{'; back = 1; break; case '(': br2 = ')'; back = 0; break; case ')': br2 = '('; back = 1; break; case '[': br2 = ']'; back = 0; break; case ']': br2 = '['; back = 1; break; default : message("matching only {},(),[]"); return; } prompt("busy"); if (back) decget(); while (1) { if (back) ch = decget(); else ch = getinc(); if (ch == EOF) break; if (ch == eol) l++; if (ch == br1) level++; if (ch == br2) { if (level) level--; else { if (!back) decget(); fil->ins = fil->sel = rloc; if (back) fil->lin -= l; else fil->lin += l; fil->ccl = fil->col = getcol(fil->ins); viewsel(); return; } } } message("no match"); } void undo(void) { BUF *b, *n, *p; /* buf, next, previous */ LINT s, l, x = 0, z = 0; /* size, lines, location, cursor */ gotobyte(0); if (fil->chg == 0) { message("nothing to undo"); return; } prompt("busy"); b = fil->stb->next; while (b != fil->stb) { n = b->next; if (!(b->del)) x += b->len; if (b->cre == fil->chg || b->del == fil->chg) { l = rloc = rofs = 0; /* count newlines */ s = fil->siz; fil->siz = b->len; fpos = fmax = 0; rbuf = b; do if (getinc() == eol) l++; while (rbuf == b); if (b->cre == fil->chg) { fil->siz = s - b->len; /* remove */ fil->nln -= l; z = x -= b->len; deletebuf(b); b = n->prev; } else { fil->siz = s + b->len; /* undelete */ fil->nln += l; z = x += b->len; b->del = 0; /* concat with previous buffer if possible */ p = b->prev; if (p->cre == b->cre && !(p->del) && !(b->del) && (b != p)) { p->len += b->len; deletebuf(b); b = p; } } /* concat with next buffer if possible */ if (b->cre == n->cre && !(b->del) && !(n->del) && (b != n)) { b->len += n->len; deletebuf(n); n = b->next; } } b = n; } gotobyte(z); fil->chg--; } void xcase(void) { BYTE buf[1024]; int n, max, pos; LINT ins, xpos, xlen, xlin, x = 0, z = 0; ins = fil->ins; xpos = cpos; xlen = clen; xlin = clin; copysel(); fil->chg++; while (x < clen) { if ((max = clen - x) > sizeof(buf)) max = sizeof(buf); if (lseek(efil, cpos + x, SEEK_SET) < 0) die("22: lseek"); pos = 0; while (pos < max) { if ((n = read(efil, &(buf[pos]), max-pos)) < 0) die("23: read"); pos += n; } for (pos = 0; pos < max; pos++) { if (islower(buf[pos])) buf[pos] -= ' '; else if (isupper(buf[pos])) buf[pos] += ' '; else z++; } if (lseek(efil, cpos + x, SEEK_SET) < 0) die("24: lseek"); pos = 0; while (pos < max) { if ((n = write(efil, &(buf[pos]), max-pos)) < 0) die("25: write"); pos += n; } x += max; } delsel(); paste(); if (x == z) { undo(); message("can only change A..Z and a..z"); } cpos = xpos; clen = xlen; clin = xlin; } /* int closefil: different levels of ask 0: close: don't ask exept when file is read only, save, close 1: close: ask for all changed files, (save), close 2: save: don't ask exept when file is read only, save, don't close 3: save as: ask for a filename, save, don't close */ int closefil(int ask) { BYTE buf[1024]; BYTE str[256]; BYTE bak[256]; BYTE lnk[256]; struct stat st, lst; int outf = -1; int n, m = 8, ch = 'y', i = -1, l = 0, pos = 0, max = 0, done = 0; if ((fil->chg && (ask==1 || fil->sta==2)) || (fil->sta==2 && ask==3)) { if (fil->sta == 0) prompt("save changes to %s? [y/n]: ", fil->name); else if (fil->sta == 1) prompt("save new file %s? [y/n]: ", fil->name); else prompt("save read-only file %s? [y/n]: ", fil->name); if (scanch(&ch, "yn")) return -1; } if (ch == 'n' || (!fil->chg && ask < 2)) { if (close(fil->file)) die("26: close %s", fil->name); if (fil->sta == 1) remove(fil->name); message("file %s closed", fil->name); freefil(0); return 0; } if (fstat(fil->file, &st)) die("27: fstat %s", fil->name); if (fil->tim && fil->tim != st.st_mtime) { message("file %s has been modified: cannot save!", fil->name); return -1; } *bak = '\0'; strcpy(str, fil->name); if (ask == 3 || (fil->chg && fil->sta == 2)) { prompt("save as: ", str); for (l = 0; str[l]; l++); if (scanstr(str, &l, sizeof(str))) return -1; if (strcmp(str, fil->name)) { /* other filename */ if (access(str, F_OK) == 0) { /* file exists */ if (access(str, W_OK) == 0) { prompt("file %s exists, overwrite? [y/n] : ", str); } else { prompt("file %s is WRITE PROTECTED, overwrite? [y/n] : ", str); } if (scanch(&ch, "ny")) return -1; if (ch == 'n') { message("save cancelled"); return -1; } } else { /* file does not exist */ outf = open(str, O_WRONLY | O_CREAT | O_EXCL, 0644); if (outf < 0) { message("cannot create %s", str); return -1; } } } else if (!fil->chg) { /* same filename but not changed */ message("file %s unchanged: no save", fil->name); return -1; } } if (outf < 0) { if (fil->sta == 1) { outf = fil->file; /* new file: already open */ } else { /* resolve symlinks, rename original, save to new file */ strcpy(lnk, str); while (--m) { strcpy(bak, lnk); if (lstat(lnk, &lst)) die("28: lstat %s", bak); if (!S_ISLNK(lst.st_mode)) break; if ((n = readlink(bak,lnk,sizeof(lnk)-1)) < 0) die("29: readlink"); lnk[n] = '\0'; } if (!m || st.st_ino != lst.st_ino) die("30: resolving symlink"); strcat(bak, "~"); if (rename(lnk, bak)) { if (fil->sta == 2) { message("cannot write to %s", lnk); return -1; } die("31: rename %s", lnk); } outf = open(lnk, O_WRONLY | O_CREAT | O_EXCL, 0644); if (outf < 0) { if (fil->sta == 2) { message("cannot write to %s!", str); return -1; } die("32: open output file %s", lnk); } } } locate(0); while (!done) { buf[max] = ch = getinc(); if (ch == EOF) done = 1; else max++; if (max >= sizeof(buf) || done) { while (pos < max) { if ((n = write(outf, &(buf[pos]), max-pos)) < 0) die("33: write"); pos += n; } pos = max = 0; if (i != rloc/(fil->siz/100+1)) { i = rloc/(fil->siz/100+1); prompt("writing %s %d%%", str, i); } } } if (fil->sta == 1) { fil->sta = 0; } else { if (fchmod(outf, st.st_mode)) die("34: fchmod"); if (getuid() == 0) { fchown(fil->file, 0, 0); fchown(outf, st.st_uid, st.st_gid); } if (close(outf)) die("35: close %s", str); } if (close(fil->file)) die("36: close %s", fil->name); if (!bakfile && *bak) remove(bak); if (ask >= 2) { strcpy(fil->name, str); freefil(1); message("file %s saved", str); } else { freefil(0); message("file %s saved and closed", str); } return 0; } void menucommand(int x) { int i, j, k; if ((i = x + 7 - scols) >= 0) { switch (i) { case 1: autoind = !autoind; break; case 3: chkcase = !chkcase; break; case 5: blackbg = !blackbg; initcolors(); break; default: return; } showmenu(); paint(); } else { for (i = j = k = 0; j <= x && menutext[j]; j++) { if (menutext[j] > ' ') { if (!k) i += k = 1; } else k = 0; } if (!menutext[j] || menutext[x] <= ' ') return; switch (i) { case 1: putkey(KEY_ALT('h')); break; case 2: putkey(KEY_ALT('w')); break; case 3: putkey(KEY_ALT('u')); break; case 4: putkey(KEY_ALT('o')); break; case 5: putkey(KEY_ALT('s')); break; case 6: putkey(KEY_ALT('q')); break; case 7: putkey(KEY_ALT('x')); break; case 8: putkey(KEY_ALT('v')); break; case 9: putkey(KEY_ALT('c')); break; case 10: putkey(KEY_ALT('b')); break; case 11: putkey(KEY_ALT('f')); break; case 12: putkey(KEY_ALT('r')); break; default: return; } } } int checkmouse(int x, int y, int event) { LINT s; FIL *f; int x0, y0, xx, yy, i; if (x < 0 || x >= scols || y < 0 || y >= slins) return 0; if (ignmouse) { if (ignmouse < 0) { ignmouse++; } else if (event & BUT_LEFT) { if (bcnt) { if (x == 0) { if (y < 2) y = 1; else y--; if ((btop = btop-y) < 0) btop = 0; putkey(KEY_HOME); } else if (y > 0 && y < slins-1 && btop+y-1 < bcnt) { bsel = btop + y - 1; putkey(KEY_HOME); putkey('\r'); } } else { putkey('\r'); ignmouse--; } } else if (event & BUT_RIGHT) { if (bcnt && x == 0) { if (y < 2) y = 1; else y--; if ((btop = btop+y) > bcnt-slins+1) { if ((btop = bcnt-slins+2) < 0) btop = 0; } putkey(KEY_HOME); } else { putkey('\033'); ignmouse--; } } else if (event & BUT_MIDDLE) { if (bcnt && x == 0) { if (y < 2) { btop = 0; } else if (y >= slins - 2) { if ((btop = bcnt-slins+2) < 0) btop = 0; } else { btop = y * (bcnt-slins+2) / (slins-2); if (btop > bcnt-slins+2) btop = bcnt-slins+2; if (btop < 0) btop = 0; } putkey(KEY_HOME); } } return 0; } if (wchange) { if (event & REL_RIGHT) { if (wchange >= 5) { y0 = y; x0 = x + 5 - wchange; yy = y0 + filck->lins; xx = x0 + filck->cols; if (x0 < 0) x0 = 0; if (yy > slins) yy = slins; if (xx > scols) xx = scols; } else { wchange--; y0 = filck->winy; x0 = filck->winx; yy = y0 + filck->lins; xx = x0 + filck->cols; if (wchange & 1) yy = y + 1; else y0 = y; if (wchange & 2) xx = x + 1; else x0 = x; } f = fil; fil = filck; resize(yy - y0, xx - x0, y0, x0); topdisp(f); } wchange = 0; return 1; } /* event in what window? */ f = filbk; filck = NULL; while (f != NULL) { x0 = x - f->winx; y0 = y - f->winy; if (x0 >= 0 && x0 < f->cols && y0 >= 0 && y0 < f->lins) { xx = x0; yy = y0; filck = f; } f = f->disp; } if (filck == NULL) { if (y == 0) { if (event & BUT_LEFT) { menucommand(x); return 0; } } return 0; } x = xx; y = yy; f = fil; /* text area */ if (y > 0 && y < filck->lins-1 && x > 0 && x < filck->cols-1) { x--; y--; if (event & BUT_LEFT) { topdisp(filck); s = lloc[y] + getofs(lloc[y], fil->sft + x); if (s >= fil->ins && s <= fil->sel) { selmode = (selmode + 1) % 3; } else { selmode = 0; } kslmode = 0; fil->ins = fil->sel = s; if (s < fil->siz) fil->lin = fil->tln + y; else fil->lin = fil->nln; expandsel(); fil->ccl = fil->col; mins = fil->ins; msel = fil->sel; mlin = fil->lin; mcol = fil->col; } else if (event & BUT_MIDDLE) { topdisp(filck); } else if (event & BUT_RIGHT) { topdisp(filck); fil->ccl = fil->col; /* save col */ s = lloc[y] + getofs(lloc[y], fil->sft + x); if (s < fil->ins) { fil->ins = s; fil->lin = fil->tln + y; s = fil->sel; expandsel(); fil->ccl = fil->col; fil->sel = s; } else { if (s == fil->sel) return 0; fil->sel = s; s = fil->ins; fil->ins = fil->sel; expandsel(); fil->col = fil->ccl; /* restore col */ fil->ins = s; } } else if (event & REL_LEFT) { /* mins, msel ... are valid */ topdisp(filck); s = lloc[y] + getofs(lloc[y], fil->sft + x); if (s == fil->ins && fil->sel == fil->ins) return 0; if (s < mins) { fil->ins = s; fil->lin = fil->tln + y; expandsel(); fil->ccl = fil->col; fil->sel = msel; } else { fil->ins = s; expandsel(); fil->ins = mins; fil->lin = mlin; fil->col = mcol; } } else { return 0; } return 1; } /* vertical scroll */ if (x == 0 && y > 0 && y < filck->lins-1) { y--; fil = filck; if (event & BUT_LEFT) { if (y) scrollup(y); else scrollup(1); } else if (event & BUT_MIDDLE) { if (y == 0) { fil->top = 0; fil->tln = 0; } else { scrolltoline((fil->nln + 2 - fil->lins/2) * y / (fil->lins-3)); } } else if (event & BUT_RIGHT) { if (y) scrolldown(y); else scrolldown(1); } else { fil = f; return 0; } paint(); fil = f; return (fil != filck); } /* horizontal scroll */ if (y == filck->lins-1 && x > 0 && x < filck->cols-1) { x--; fil = filck; if (event & BUT_LEFT) { if (x) scrolltocol(fil->sft - x); else scrolltocol(fil->sft - 1); } else if (event & BUT_MIDDLE) { scrolltocol((ncol + 2 - fil->cols/2) * x / (fil->cols-3)); } else if (event & BUT_RIGHT) { if (x) scrolltocol(fil->sft + x); else scrolltocol(fil->sft + 1); } else { fil = f; return 0; } paint(); fil = f; return (fil != filck); } /* search bar */ if (x == filck->cols-1 && y > 0 && y < filck->lins-1) { if (event & BUT_LEFT) { srchlen = seltostr(srchstr, sizeof(srchstr)); fil = filck; i = searchup(); fil = f; if (i) topdisp(filck); } else if (event & BUT_RIGHT) { srchlen = seltostr(srchstr, sizeof(srchstr)); fil = filck; if (fil != f) i = 0; else i = 1; i = searchdown(srchstr, srchlen, i); fil = f; if (i) topdisp(filck); } else { return 0; } return 1; } /* title bar: top, move */ if (y == 0 && x > 0 && x < filck->cols-1) { if (event & BUT_LEFT) { topdisp(filck); } else if (event & BUT_RIGHT) { wchange = 5 + x; prompt ("move window .."); } return 0; } /* corner: resize */ if (event & BUT_RIGHT) { if (y) wchange += 1; if (x) wchange += 2; wchange++; prompt ("resize window .."); } return 0; } #ifdef WITH_GPM int gpm_mousehandler(Gpm_Event *event, void *data) { int me = 0; if (filst == NULL && !bcnt) { prompt("mouse (gpm): %d/%d buttons:%d type:%d", event->x, event->y, event->buttons, event->type); return 0; } if (event->buttons & GPM_B_LEFT && event->type & GPM_DOWN) { me = BUT_LEFT; } else if (event->buttons & GPM_B_MIDDLE && event->type & GPM_DOWN) { me = BUT_MIDDLE; } else if (event->buttons & GPM_B_RIGHT && event->type & GPM_DOWN) { me = BUT_RIGHT; } else if (event->buttons & GPM_B_LEFT && event->type & GPM_UP) { me = REL_LEFT; } else if (event->buttons & GPM_B_LEFT && event->type & GPM_DRAG) { me = REL_LEFT; } else if (event->buttons & GPM_B_RIGHT && event->type & GPM_UP) { me = REL_RIGHT; /* } else if (event->buttons & GPM_B_RIGHT && event->type & GPM_DRAG) { me = REL_RIGHT; */ } else return 0; if (checkmouse(event->x - 1, event->y - 1, me)) paint(); return 01000; } #endif #ifdef NCURSES_MOUSE_VERSION int ncurses_mousehandler(void) { MEVENT event; int me; getmouse(&event); if (filst == NULL && !bcnt) { prompt("mouse (ncurses): %d/%d bstate: 0%o", event.x, event.y, event.bstate); return 0; } if (event.bstate & BUTTON1_PRESSED) me = BUT_LEFT; else if (event.bstate & BUTTON2_PRESSED) me = BUT_MIDDLE; else if (event.bstate & BUTTON3_PRESSED) me = BUT_RIGHT; else if (event.bstate & BUTTON1_RELEASED) me = REL_LEFT; else if (event.bstate & BUTTON3_RELEASED) me = REL_RIGHT; else return 0; if (checkmouse(event.x, event.y, me)) paint(); return 0; } #endif void initmouse(void) { if (!mouseon || mouseup) return; #ifdef WITH_GPM gpmconn.eventMask = ~0; gpmconn.defaultMask = 0; gpmconn.maxMod = 0; gpmconn.minMod = 0; if (Gpm_Open(&gpmconn, 0) != -1) { gpm_handler = gpm_mousehandler; gpm_visiblepointer = 1; mouseup = 1; } else { #endif mouseup = 0; #ifdef NCURSES_MOUSE_VERSION mousemask(REPORT_MOUSE_POSITION | ALL_MOUSE_EVENTS, NULL); mouseinterval(1); mouseup = 2; #endif #ifdef WITH_GPM } #endif } void winchhandler(void) { FIL *f; int x0, y0, xx, yy, lins, cols; endmouse(); endwin(); wrefresh(curscr); lins = slins; cols = scols; getmaxyx(stdscr, slins, scols); delwin(msgwin); msgwin = newwin(1, scols, slins-1, 0); keypad(msgwin, TRUE); if (hascolor) { wbkgdset(msgwin, COLOR_PAIR(5) | ' '); wattrset(msgwin, COLOR_PAIR(5)); } showmenu(); f = fil; fil = filst; while (fil != NULL) { y0 = fil->winy; yy = y0 + fil->lins + slins - lins; if (yy - y0 < 3) yy = y0 + 3; if (yy > slins) { y0 = y0 + slins - yy; yy = slins; } if (y0 < 0) y0 = 0; x0 = fil->winx; xx = x0 + fil->cols + scols - cols; if (xx - x0 < 10) xx = x0 + 10; if (xx > scols) { x0 = x0 + scols - xx; xx = scols; } if (x0 < 0) x0 = 0; resize(yy - y0, xx - x0, y0, x0); paint(); fil = fil->next; } fil = f; message("screen size %d %d", slins, scols); paint(); initmouse(); } /* void splitargs: support for \, " and ', ends at '\n' and ';' cnt should indicate maximum arg count and returns with arg count str is destroyed: whitespace is zeroed, \ and quotes are deleted arg list filled with argument pointers, arg[cnt] is set to NULL */ void splitargs(BYTE *str, BYTE **arg, int *cnt) { int max; BYTE *p, *s, q = '\0'; max = *cnt - 1; *cnt = 0; p = str; while (*p == ' ' || *p == '\t') p++; arg[*cnt] = p; while (*p) { if (q) { if (*p == q) { q = '\0'; for (s = p; *s; s++) *s = *(s+1); } else if (*p) p++; } if (!q) { if (*p == '\\') { for (s = p; *s; s++) *s = *(s+1); p++; } else if (*p == '\"' || *p == '\'') { q = *p; for (s = p; *s; s++) *s = *(s+1); } else if (*p == ' ' || *p == '\t') { *p = '\0'; (*cnt)++; p++; if (*cnt >= max) die("37: too many args"); while (*p == ' ' || *p == '\t') p++; arg[*cnt] = p; } else if (*p == ';' || *p == '\n') { *p = '\0'; } else if (*p) p++; } } if (*arg[*cnt] > '\0') (*cnt)++; arg[*cnt] = NULL; } void scanparams(int cnt, BYTE **arg, int init) { int i = 0, j = 0, k = 0, l, m, n; FIL *f = NULL, *x = NULL; BYTE str[128]; str[0] = '\0'; while (i < cnt) { if (arg[i][0] == '-') { j = 0; do { j++; l = eol; switch (arg[i][j]) { case 'a': autoind = 0; break; case 'A': autoind = 1; break; case 'b': bakfile = 0; break; case 'B': bakfile = 1; break; case 'c': chkcase = 0; break; case 'C': chkcase = 1; break; case 'x': expandw = 0; break; case 'X': expandw = 1; break; case 'p': mouseon = 0; endmouse(); break; case 'P': mouseon = 1; initmouse(); break; case 'k': blackbg = 0; initcolors(); k = 1; break; case 'K': blackbg = 1; initcolors(); k = 1; break; case 'i': if (arg[i][++j]) strcpy(indstr, &arg[i][j]); else if (++i < cnt) strcpy(indstr, arg[i]); else indstr[0] = '\0'; j = 0; break; case 'm': if (arg[i][++j]) strcpy(makstr, &arg[i][j]); else if (++i < cnt) strcpy(makstr, arg[i]); else makstr[0] = '\0'; j = 0; break; case 'r': if (arg[i][++j]) strcpy(runstr, &arg[i][j]); else if (++i < cnt) strcpy(runstr, arg[i]); else runstr[0] = '\0'; j = 0; break; case 't': if (arg[i][++j]) m = sscanf(&arg[i][j], "%i%1s", &n, str); else if (++i < cnt) m = sscanf(&arg[i][j=0], "%i%1s", &n, str); else m = 0; if (m != 1 || n < 0 || n > 100) { strcpy(str, "tab size not valid: "); strcat(str, &arg[i][j]); } else { tabsize = n; str[0] = '\0'; j = 0; k = 1; if (!init && filst != NULL) { fil->ccl = fil->col = getcol(fil->ins); viewsel(); } } break; case 'U': dosmode = 0; eol = '\n'; break; case 'D': dosmode = 1; eol = '\n'; break; case 'M': dosmode = 0; eol = '\r'; break; default: if (arg[i][j] || j <= 1) { strcpy(str, "unknown option -"); strcat(str, &arg[i][j]); str[17] = '\0'; } else j = 0; break; } if (str[0]) { if (init) finish("%s\n%s", str, optntext); else message(str); return; } if (l != eol) rereadall(); } while (j); } else { if (openfil(arg[i])) { if (init) finish(mesgstr); return; } putdisp(fil, x); if (f == NULL) f = fil; x = fil; } i++; } if (f == NULL) f = fil; else k = 1; if (k) fil = filst; else fil = f; while (fil != NULL) { paint(); fil = fil->next; } fil = f; showmenu(); paint(); } void openfile(void) { BYTE str[256], curpath[256], *arg[32]; int i, x, y, p, ch, mous = 0, len = 0, cnt = 32; FILE *f = NULL, *fff = NULL, *tmp = NULL; struct stat st; bcnt = 1; while (bcnt) { if (tmp == NULL) { p = slins - 2; bsel = btop = x = y = 0; if (getcwd(curpath, sizeof(curpath)) == NULL) die("38: getcwd"); if ((tmp = tmpfile()) == NULL) die("39: open tmp file"); if ((fff = tmpfile()) == NULL) die("40: open fff file"); if ((f = popen("ls -a1", "r")) == NULL) die("41: popen"); fputc('.', tmp); fputc('.', tmp); fputc('/', tmp); fputc('\0', tmp); while ((ch = fgetc(f)) != EOF) { if (ch == '\n') { str[x] = '\0'; if (stat(str, &st)) st.st_mode = 0; if (str[0] == '.' && !str[1]) st.st_mode = 0; if (str[0] == '.' && str[1] == '.' && !str[2]) st.st_mode = 0; if (S_ISDIR(st.st_mode)) { for (i = 0; i < x; i++) fputc(str[i], tmp); fputc('/', tmp); fputc('\0', tmp); y++; } else if (S_ISREG(st.st_mode)) { for (i = 0; i <= x; i++) fputc(str[i], fff); y++; } x = 0; } else { if (x < sizeof(str)-2) str[x++] = ch; } } pclose(f); if (fseek(fff, 0, SEEK_SET)) die("42: fseek fff"); while ((ch = fgetc(fff)) != EOF) fputc(ch, tmp); if (fclose(fff)) die("43: close fff"); bcnt = y + 1; } clear(); if (hascolor) wattrset(stdscr, COLOR_PAIR(2)); if (fseek(tmp, 0, SEEK_SET)) die("44: fseek tmp"); len = 0; for (y = 0; y < bcnt; y++) { x = 0; while ((ch = fgetc(tmp)) > 0) { if ((y >= btop) && (y < btop + p)) { if (!x) wmove(stdscr, y + 1 - btop, 1); i = A_REVERSE; if (y == bsel) str[len++] = ch; else i = 0; if (x < scols) waddch(stdscr, dispmap[ch] | i); x++; } } } if (hascolor) wattrset(stdscr, COLOR_PAIR(7)); wattron(stdscr, A_REVERSE); mvwhline(stdscr, 0, 0, ' ', scols); mvwprintw(stdscr, 0, 0, " path: %s ", curpath); mvwvline(stdscr, 1, 0, ACS_VLINE, slins-2); i = slins-2; mvwvline(stdscr, (btop*i)/bcnt + 1, 0, ACS_CKBOARD, (i*i)/bcnt + 1); wattroff(stdscr, A_REVERSE); wrefresh(stdscr); prompt("files, options: "); if (scanstr(str, &len, sizeof(str))) break; if (len > 0) { splitargs(str, arg, &cnt); if (cnt == 1) { if (stat(arg[0], &st)) st.st_mode = 0; if (S_ISDIR(st.st_mode)) { if (chdir(arg[0])) { message("cannot change to %s", arg[0]); break; } else { if (fclose(tmp)) die("45: close tmp"); tmp = NULL; } } } if (tmp) { if (mous) ignmouse--; scanparams(cnt, arg, 0); break; } } mous = 0; if (len < 0) { if (len >= -4 && (bsel < btop || bsel > btop + p)) { bsel = btop; len = -9; } if (len == -1) { if (bsel > 0) bsel--; if (bsel < btop) btop--; } else if (len == -2) { if (bsel < bcnt - 1) bsel++; if (bsel >= btop + p) btop++; } else if (len == -3) { if (btop - p > 0) btop -= p; else btop = 0; if (bsel - p > 0) bsel -= p; else bsel = 0; } else if (len == -4) { if (btop + p < bcnt - p) btop += p; else btop = bcnt - p; if (btop < 0) btop = 0; if (bsel + p < bcnt - 1) bsel += p; else bsel = bcnt - 1; } else if (len == -5) { mous = 1; } else if (len == -6) { prompt("delete %s? [y/n]: ", str); scanch(&ch, "ny"); if (ch == 'y') { if (unlink(str)) { prompt("Error: %s not deleted!", str); getkey(); } else { if (fclose(tmp)) die("45: close tmp"); tmp = NULL; } } } else if (len == -7) { } else if (len == -8) { } else if (len == -9) { } else break; } } bcnt = 0; if (tmp) fclose(tmp); if (chdir(pathstr)) die("46: chdir"); showmenu(); } void padexec(void) { char cmd[512]; int ch; FILE *f = NULL; prompt("save & make [m], run [r], exec sel [x], shell [s] : ", cmd); if (scanch(&ch, "mrxs")) return; if (ch == 'm') { strcpy(cmd, makstr); strcat(cmd, " "); strcat(cmd, fil->name); strcat(cmd, " 2>&1"); } else if (ch == 'r') { strcpy(cmd, runstr); strcat(cmd, " "); strcat(cmd, fil->name); } else if (ch == 's') { strcpy(cmd, "sh"); } else if (ch == 'x') { seltostr(cmd, sizeof(cmd)); } else return; ignmouse = 1; if (ch == 'm') closefil(2); paint(); clear(); refresh(); endwin(); printf("\nrunning: %s\n\n", cmd); fflush(stdout); if (ch == 'm') { if ((fil->lst = tmpfile()) == NULL) die("47: open list file"); if ((f = popen(cmd, "r")) == NULL) die("48: popen %s", cmd); while ((ch = fgetc(f)) != EOF) { fputc(ch, stdout); if (ch == '\n') fflush(stdout); fputc(ch, fil->lst); } pclose(f); if (fseek(fil->lst, 0, SEEK_SET)) die("49: fseek lst"); } else { system(cmd); } if (ch != 's') { printf("\nENTER to continue .."); fflush(stdout); fgets(cmd, sizeof(cmd), stdin); } cursvis = (curs_set(0) == ERR); showmenu(); ignmouse--; } LINT nexterror(void) { char str[256]; int ch, i = 0, l = 0; LINT x = 0; if (fil->lst == NULL) { message("no error list"); return -1; } if ((ch = fgetc(fil->lst)) == EOF) { message("no more errors"); return -1; } while (ch != '\n' && ch != EOF) { if (!i) { if (x < 0 && ch == ':') x = 0; else if (ch == ':') i = 1; else if (ch >= '0' && ch <= '9') x = x * 10 + ch - '0'; else x = -1; } else if (l < sizeof(str)-1) { str[l] = ch; l++; } ch = fgetc(fil->lst); } if (i) { prompt("busy"); gotoline(x); str[l] = '\0'; message("%ld:%s", x, str); return x; } return 0; } void kselrcl(void) { LINT x; if (fil->ins == mins) { fil->ins = msel; x = mlin; mlin = fil->lin; fil->lin = x; x = mcol; mcol = fil->col; fil->col = x; } else { fil->sel = fil->ins; } } void kselsto(void) { LINT x; if (fil->ins >= mins) { msel = fil->ins; fil->sel = fil->ins; fil->ins = mins; x = mlin; mlin = fil->lin; fil->lin = x; x = mcol; mcol = fil->col; fil->col = x; } else { fil->sel = mins; } } int getkey(void) { int key = 01000, n = 0, k; if (mpos >= 0 && mpos < mcnt) return mbuf[mpos++]; while (key >= 01000) { if (kpos != kcnt) return kbuf[kpos = (kpos + 1) % 16]; if (mouseup != 1) key = getch(); /* wait for key or mouse input */ #ifdef WITH_GPM else key = Gpm_Getch(); /* wait for key, mouse handler asynchr. */ #endif #ifdef NCURSES_MOUSE_VERSION if (mouseup == 2 && key == KEY_MOUSE) { ncurses_mousehandler(); key = 01000; } #endif } if (key == '\033') { /* ESC ! */ prompt(""); timeout(0); /* keys immediately after ESC */ while ((k = getch()) >= 0) { waddch(msgwin, k | A_REVERSE); key = k; n++; } timeout(-1); showmsg(); if (n > 1) return -1; if (ignmouse) { key = '\033'; } else { if (n == 0) key = getch(); k = key | ' '; if (k >= '`' && k <= 'z') key = k | 128; else return '\033'; } } k = key | ' '; if (k >= ('`' | 128) && k <= ('z' | 128)) key = KEY_ALT((k & 127)); if (key >= FUNKEY) key = fkeymap[key-FUNKEY]; if (mpos == -1) { if (key == KEY_ALT('k') || key == KEY_ALT('p') || mcnt >= 256) { prompt("end recording: %d keys", mcnt); mpos = mcnt; return -1; } mbuf[mcnt] = key; mcnt++; } return key; } void debug(void) { #if PAD_DEBUG char str[64], s[64]; int len = 0; BUF *x; str[0] = '\0'; x = fil->stb->next; while (x != fil->stb) { len += sprintf(s, "%ld:%ld:%d:%d ", x->pos, x->len, x->cre, x->del); if (len > 55) { strcat(str, "..."); break; } strcat(str, s); x = x->next; } message("(pos:len:cre:del) %s", str); #endif } int checkkey(int key) { LINT x; int i; if (key < 0) return 0; switch (key) { /* check these keys even when no file is open: */ case '\033': return 1; case KEY_ALT('o'): case KEY_F(2): case KEY_OPEN: openfile(); return 1; case KEY_ALT('e'): case KEY_F(9): fil = filst; while (filst != NULL) if (closefil(0)) return 1; done = 1; return 0; case KEY_ALT('q'): case KEY_F(10): case KEY_EXIT: fil = filst; while (filst != NULL) if (closefil(1)) return 1; done = 1; return 0; case KEY_CTRL('a'): ignmouse = 1; showscreen("ascii table", ascitext); i = getkey(); if (i == KEY_ALT('o') || i == KEY_F(2) || i == KEY_OPEN) putkey(i); if (i == KEY_ALT('h') || i == KEY_F(1) || i == KEY_HELP) putkey(i); showmenu(); ignmouse--; return 1; default: if (filst == NULL) { message("key 0%o", key); return 1; } break; } switch (key) { /* keys that don't reset kslmode */ case KEY_LEFT: case KEY_CTRL('l'): if (kslmode) kselrcl(); winhome(); locate(fil->ins); if (decget() != eol) gotobyte(fil->ins-1); else fil->ccl = fil->col; selmode = 0; expandsel(); viewsel(); if (kslmode) kselsto(); return 1; case KEY_RIGHT: case KEY_CTRL('r'): if (kslmode) kselrcl(); winhome(); locate(fil->sel); if (getinc() == eol) gotobyte(fil->sel); else gotobyte(fil->sel+1); selmode = 0; expandsel(); viewsel(); if (kslmode) kselsto(); return 1; case KEY_UP: case KEY_CTRL('u'): if (kslmode) kselrcl(); winhome(); if (fil->lin) { if (fil->lin == fil->tln) scrollup(1); fil->lin--; paint(); x = lloc[fil->lin-fil->tln]; fil->ins = x + getofs(x, fil->ccl); } if (selmode != 2) selmode = 0; expandsel(); viewsel(); if (kslmode) kselsto(); return 1; case KEY_DOWN: case KEY_CTRL('d'): if (kslmode) kselrcl(); if (fil->ins != fil->sel) { if (!dosmode) gotobyte(fil->sel); else if (fil->ins < fil->sel-1 && chcod != eol) gotobyte(fil->sel); } winhome(); if (fil->lin < fil->nln) { if (fil->lin >= fil->tln+fil->lins-3) scrolldown(1); fil->lin++; paint(); x = lloc[fil->lin-fil->tln]; fil->ins = x + getofs(x, fil->ccl); } if (selmode != 2) selmode = 0; expandsel(); viewsel(); if (kslmode) kselsto(); return 1; case KEY_NPAGE: case KEY_NEXT: case KEY_CTRL('n'): case KEY_C3: if (kslmode) kselrcl(); winhome(); scrolldown(fil->lins-2); fil->lin += fil->lins-2; if (fil->lin > fil->nln) fil->lin = fil->nln; paint(); x = lloc[fil->lin-fil->tln]; fil->ins = x + getofs(x, fil->ccl); if (selmode != 2) selmode = 0; expandsel(); viewsel(); if (kslmode) kselsto(); return 1; case KEY_PPAGE: case KEY_PREVIOUS: case KEY_CTRL('p'): case KEY_A3: if (kslmode) kselrcl(); winhome(); scrollup(fil->lins-2); fil->lin -= fil->lins-2; if (fil->lin < 0) fil->lin = 0; paint(); x = lloc[fil->lin-fil->tln]; fil->ins = x + getofs(x, fil->ccl); if (selmode != 2) selmode = 0; expandsel(); viewsel(); if (kslmode) kselsto(); return 1; case KEY_ALT('t'): case KEY_F(5): case KEY_MARK: case KEY_SELECT: selmode = 0; if (kslmode) { kslmode = 0; message("selection complete"); } else { kslmode = 1; mins = fil->ins; msel = fil->ins; mlin = fil->lin; mcol = fil->col; message("keyboard selection mode"); } return 1; default: kslmode = 0; break; } switch (key) { /* keys that don't reset selmode */ case KEY_ALT('x'): case KEY_DC: viewsel(); copysel(); fil->chg++; delsel(); expandsel(); viewsel(); fil->ccl = fil->col; return 1; case KEY_ALT('c'): case KEY_COPY: viewsel(); if (fil->ins < fil->siz) { copysel(); message("selection copied"); } return 1; case KEY_ALT('d'): viewsel(); fil->chg++; delsel(); expandsel(); viewsel(); fil->ccl = fil->col; return 1; case KEY_ALT('v'): case KEY_IC: if (clen) { fil->chg++; paste(); } return 1; default: selmode = 0; break; } if ((key >= ' ' && key < 127) || key == '\t') { /* text chars */ inschar(key); return 1; } switch (key) { /* typed text */ case '\012': /* new line */ case '\015': if (autoind) { if (viewsel() >= 2) paint(); x = lloc[fil->lin-fil->tln]; } if (dosmode) inschar('\r'); inschar(eol); while (autoind) { locate(x); i = getinc(); if (i != ' ' && i != '\t') return 1; inschar(i); x++; } return 1; case KEY_ALT('a'): /* binary input */ prompt("ASCII code (024=20=0x14): "); if (scanlong(&x)) return 1; if (x >= 0 && x <= 255) { inschar(x); } else { message("code out of range"); } return 1; case KEY_BACKSPACE: case 0177: case 010: if (delchar() == eol && dosmode) { locate(fil->ins); if (decget() == '\r') delchar(); } return 1; default: break; } switch (key) { /* others: positioning/changes/fclose/displays: */ case KEY_ALT('b'): prompt("search backwards: "); if (scanstr(srchstr, &srchlen, sizeof(srchstr))) return 1; searchup(); return 1; case KEY_CTRL('b'): searchup(); return 1; case KEY_ALT('f'): case KEY_FIND: prompt("search forwards: "); if (scanstr(srchstr, &srchlen, sizeof(srchstr))) return 1; searchdown(srchstr, srchlen, 1); return 1; case KEY_CTRL('f'): searchdown(srchstr, srchlen, 1); return 1; case KEY_ALT('m'): matchbracket(); return 1; case KEY_BEG: case KEY_HOME: case KEY_A1: gotobyte(0); return 1; case KEY_END: case KEY_C1: gotobyte(fil->siz); return 1; case KEY_ALT('g'): prompt("goto byte (02000=1024=0x400): "); if (scanlong(&x)) return 1; prompt("busy"); gotobyte(x); return 1; case KEY_ALT('l'): prompt("goto line: "); if (scanlong(&x)) return 1; prompt("busy"); gotoline(x); return 1; case KEY_ALT('n'): case KEY_F(11): while(!(x = nexterror())); return 1; case KEY_ALT('r'): searchreplace(); return 1; case KEY_ALT('i'): indent(); return 1; case KEY_ALT('j'): outdent(); return 1; case KEY_ALT('u'): undo(); return 1; case KEY_CTRL('`'): xcase(); return 1; case KEY_ALT('k'): mpos = -1; mcnt = 0; message("recording keys"); return 1; case KEY_ALT('p'): mpos = 0; return 1; case KEY_ALT('w'): case KEY_F(8): if (fil->next == NULL) topdisp(filst); else topdisp(fil->next); return 0; case KEY_ALT('s'): case KEY_F(4): case KEY_SAVE: closefil(3); return 1; case KEY_ALT('z'): case KEY_F(3): case KEY_CLOSE: closefil(1); return 1; case KEY_ALT('`'): case KEY_F(12): padexec(); return 1; case KEY_ALT('y'): case KEY_F(6): message( "size:%ld lines:%ld (bufcnt:%d ncol:%ld tln:%ld sft:%ld chg:%d)", fil->siz, fil->nln, bufcnt, ncol, fil->tln, fil->sft, fil->chg); return 1; case KEY_ALT('h'): case KEY_F(1): case KEY_HELP: ignmouse = 1; showscreen("pad help", helptext); i = getkey(); if (i == KEY_ALT('o') || i == KEY_F(2) || i == KEY_OPEN) putkey(i); if (i == KEY_CTRL('a')) putkey(i); showmenu(); ignmouse--; return 1; case KEY_CTRL('y'): debug(); return 1; default: message("key 0%o", key); break; } return 1; } int main(int argc, char **argv) { char *s1, *s2; FILE *f; int key; signal(SIGINT, SIG_IGN); /* ignore ^C */ signal(SIGWINCH, (void *) winchhandler); /* xterm resizing */ initcurses(); if (initfkeymap()) die("50: initfkeymap"); initdispmap(); if (getcwd(pathstr, sizeof(pathstr)) == NULL) die("51: getcwd"); if ((f = tmpfile()) == NULL) die("52: open edit file"); efil = fileno(f); efilwrite("*", 1); /* 0 - epos must be negativ */ progname = argv[0]; strcpy(indstr, " "); strcpy(makstr, "padmak"); strcpy(runstr, "padrun"); if (!isatty(0)) finish("input not from terminal"); scanparams(argc-1, (BYTE**)&(argv[1]), 1); if (mouseon) initmouse(); if (mouseup) s1 = ""; else s1 = ", no mouse"; if (filst == NULL) s2 = ""; else s2 = ", ESC h for help"; message("%s%s%s", startmsg, s1, s2); paint(); while (!done) { key = getkey(); if (checkkey(key)) paint(); } finish(""); return 0; }