vi: rearrange functions, no logic changes
No binary (build failed)
—
(log yok)
| @@ -693,424 +693,700 @@ static ALWAYS_INLINE int query_screen_dimensions(void) | |
| } | |
| #endif | |
| - | //----- The Colon commands ------------------------------------- |
| - | #if ENABLE_FEATURE_VI_COLON |
| - | static char *get_one_address(char *p, int *addr) // get colon addr, if present |
| + | // sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready) |
| + | static int mysleep(int hund) |
| { | |
| - | int st; |
| - | char *q; |
| - | IF_FEATURE_VI_YANKMARK(char c;) |
| - | IF_FEATURE_VI_SEARCH(char *pat;) |
| + | struct pollfd pfd[1]; |
| - | *addr = -1; // assume no addr |
| - | if (*p == '.') { // the current line |
| - | p++; |
| - | q = begin_line(dot); |
| - | *addr = count_lines(text, q); |
| - | } |
| - | #if ENABLE_FEATURE_VI_YANKMARK |
| - | else if (*p == '\'') { // is this a mark addr |
| - | p++; |
| - | c = tolower(*p); |
| - | p++; |
| - | if (c >= 'a' && c <= 'z') { |
| - | // we have a mark |
| - | c = c - 'a'; |
| - | q = mark[(unsigned char) c]; |
| - | if (q != NULL) { // is mark valid |
| - | *addr = count_lines(text, q); |
| - | } |
| - | } |
| - | } |
| - | #endif |
| - | #if ENABLE_FEATURE_VI_SEARCH |
| - | else if (*p == '/') { // a search pattern |
| - | q = strchrnul(++p, '/'); |
| - | pat = xstrndup(p, q - p); // save copy of pattern |
| - | p = q; |
| - | if (*p == '/') |
| - | p++; |
| - | q = char_search(dot, pat, (FORWARD << 1) | FULL); |
| - | if (q != NULL) { |
| - | *addr = count_lines(text, q); |
| - | } |
| - | free(pat); |
| - | } |
| - | #endif |
| - | else if (*p == '$') { // the last line in file |
| - | p++; |
| - | q = begin_line(end - 1); |
| - | *addr = count_lines(text, q); |
| - | } else if (isdigit(*p)) { // specific line number |
| - | sscanf(p, "%d%n", addr, &st); |
| - | p += st; |
| - | } else { |
| - | // unrecognized address - assume -1 |
| - | *addr = -1; |
| + | if (hund != 0) |
| + | fflush_all(); |
| + | |
| + | pfd[0].fd = STDIN_FILENO; |
| + | pfd[0].events = POLLIN; |
| + | return safe_poll(pfd, 1, hund*10) > 0; |
| + | } |
| + | |
| + | //----- Set terminal attributes -------------------------------- |
| + | static void rawmode(void) |
| + | { |
| + | // no TERMIOS_CLEAR_ISIG: leave ISIG on - allow signals |
| + | set_termios_to_raw(STDIN_FILENO, &term_orig, TERMIOS_RAW_CRNL); |
| + | erase_char = term_orig.c_cc[VERASE]; |
| + | } |
| + | |
| + | static void cookmode(void) |
| + | { |
| + | fflush_all(); |
| + | tcsetattr_stdin_TCSANOW(&term_orig); |
| + | } |
| + | |
| + | //----- Terminal Drawing --------------------------------------- |
| + | // The terminal is made up of 'rows' line of 'columns' columns. |
| + | // classically this would be 24 x 80. |
| + | // screen coordinates |
| + | // 0,0 ... 0,79 |
| + | // 1,0 ... 1,79 |
| + | // . ... . |
| + | // . ... . |
| + | // 22,0 ... 22,79 |
| + | // 23,0 ... 23,79 <- status line |
| + | |
| + | //----- Move the cursor to row x col (count from 0, not 1) ------- |
| + | static void place_cursor(int row, int col) |
| + | { |
| + | char cm1[sizeof(ESC_SET_CURSOR_POS) + sizeof(int)*3 * 2]; |
| + | |
| + | if (row < 0) row = 0; |
| + | if (row >= rows) row = rows - 1; |
| + | if (col < 0) col = 0; |
| + | if (col >= columns) col = columns - 1; |
| + | |
| + | sprintf(cm1, ESC_SET_CURSOR_POS, row + 1, col + 1); |
| + | write1(cm1); |
| + | } |
| + | |
| + | //----- Erase from cursor to end of line ----------------------- |
| + | static void clear_to_eol(void) |
| + | { |
| + | write1(ESC_CLEAR2EOL); |
| + | } |
| + | |
| + | static void go_bottom_and_clear_to_eol(void) |
| + | { |
| + | place_cursor(rows - 1, 0); |
| + | clear_to_eol(); |
| + | } |
| + | |
| + | //----- Start standout mode ------------------------------------ |
| + | static void standout_start(void) |
| + | { |
| + | write1(ESC_BOLD_TEXT); |
| + | } |
| + | |
| + | //----- End standout mode -------------------------------------- |
| + | static void standout_end(void) |
| + | { |
| + | write1(ESC_NORM_TEXT); |
| + | } |
| + | |
| + | //----- Text Movement Routines --------------------------------- |
| + | static char *begin_line(char *p) // return pointer to first char cur line |
| + | { |
| + | if (p > text) { |
| + | p = memrchr(text, '\n', p - text); |
| + | if (!p) |
| + | return text; |
| + | return p + 1; |
| } | |
| return p; | |
| } | |
| - | static char *get_address(char *p, int *b, int *e) // get two colon addrs, if present |
| + | static char *end_line(char *p) // return pointer to NL of cur line |
| { | |
| - | //----- get the address' i.e., 1,3 'a,'b ----- |
| - | // get FIRST addr, if present |
| - | while (isblank(*p)) |
| - | p++; // skip over leading spaces |
| - | if (*p == '%') { // alias for 1,$ |
| - | p++; |
| - | *b = 1; |
| - | *e = count_lines(text, end-1); |
| - | goto ga0; |
| - | } |
| - | p = get_one_address(p, b); |
| - | while (isblank(*p)) |
| - | p++; |
| - | if (*p == ',') { // is there a address separator |
| - | p++; |
| - | while (isblank(*p)) |
| - | p++; |
| - | // get SECOND addr, if present |
| - | p = get_one_address(p, e); |
| + | if (p < end - 1) { |
| + | p = memchr(p, '\n', end - p - 1); |
| + | if (!p) |
| + | return end - 1; |
| } | |
| - | ga0: |
| - | while (isblank(*p)) |
| - | p++; // skip over trailing spaces |
| return p; | |
| } | |
| - | #if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS |
| - | static void setops(const char *args, const char *opname, int flg_no, |
| - | const char *short_opname, int opt) |
| + | static char *dollar_line(char *p) // return pointer to just before NL line |
| { | |
| - | const char *a = args + flg_no; |
| - | int l = strlen(opname) - 1; // opname have + ' ' |
| + | p = end_line(p); |
| + | // Try to stay off of the Newline |
| + | if (*p == '\n' && (p - begin_line(p)) > 0) |
| + | p--; |
| + | return p; |
| + | } |
| - | // maybe strncmp? we had tons of erroneous strncasecmp's... |
| - | if (strncasecmp(a, opname, l) == 0 |
| - | || strncasecmp(a, short_opname, 2) == 0 |
| - | ) { |
| - | if (flg_no) |
| - | vi_setops &= ~opt; |
| - | else |
| - | vi_setops |= opt; |
| - | } |
| + | static char *prev_line(char *p) // return pointer first char prev line |
| + | { |
| + | p = begin_line(p); // goto beginning of cur line |
| + | if (p > text && p[-1] == '\n') |
| + | p--; // step to prev line |
| + | p = begin_line(p); // goto beginning of prev line |
| + | return p; |
| } | |
| - | #endif |
| - | #endif /* FEATURE_VI_COLON */ |
| + | static char *next_line(char *p) // return pointer first char next line |
| + | { |
| + | p = end_line(p); |
| + | if (p < end - 1 && *p == '\n') |
| + | p++; // step to next line |
| + | return p; |
| + | } |
| - | // buf must be no longer than MAX_INPUT_LEN! |
| - | static void colon(char *buf) |
| + | //----- Text Information Routines ------------------------------ |
| + | static char *end_screen(void) |
| { | |
| - | #if !ENABLE_FEATURE_VI_COLON |
| - | // Simple ":cmd" handler with minimal set of commands |
| - | char *p = buf; |
| + | char *q; |
| int cnt; | |
| - | if (*p == ':') |
| - | p++; |
| - | cnt = strlen(p); |
| - | if (cnt == 0) |
| - | return; |
| - | if (strncmp(p, "quit", cnt) == 0 |
| - | || strncmp(p, "q!", cnt) == 0 |
| - | ) { |
| - | if (modified_count && p[1] != '!') { |
| - | status_line_bold("No write since last change (:%s! overrides)", p); |
| - | } else { |
| - | editing = 0; |
| - | } |
| - | return; |
| + | // find new bottom line |
| + | q = screenbegin; |
| + | for (cnt = 0; cnt < rows - 2; cnt++) |
| + | q = next_line(q); |
| + | q = end_line(q); |
| + | return q; |
| + | } |
| + | |
| + | // count line from start to stop |
| + | static int count_lines(char *start, char *stop) |
| + | { |
| + | char *q; |
| + | int cnt; |
| + | |
| + | if (stop < start) { // start and stop are backwards- reverse them |
| + | q = start; |
| + | start = stop; |
| + | stop = q; |
| } | |
| - | if (strncmp(p, "write", cnt) == 0 |
| - | || strncmp(p, "wq", cnt) == 0 |
| - | || strncmp(p, "wn", cnt) == 0 |
| - | || (p[0] == 'x' && !p[1]) |
| - | ) { |
| - | if (modified_count != 0 || p[0] != 'x') { |
| - | cnt = file_write(current_filename, text, end - 1); |
| - | } |
| - | if (cnt < 0) { |
| - | if (cnt == -1) |
| - | status_line_bold("Write error: "STRERROR_FMT STRERROR_ERRNO); |
| - | } else { |
| - | modified_count = 0; |
| - | last_modified_count = -1; |
| - | status_line("'%s' %dL, %dC", |
| - | current_filename, |
| - | count_lines(text, end - 1), cnt |
| - | ); |
| - | if (p[0] == 'x' |
| - | || p[1] == 'q' || p[1] == 'n' |
| - | || p[1] == 'Q' || p[1] == 'N' |
| - | ) { |
| - | editing = 0; |
| - | } |
| - | } |
| - | return; |
| - | } |
| - | if (strncmp(p, "file", cnt) == 0) { |
| - | last_status_cksum = 0; // force status update |
| - | return; |
| - | } |
| - | if (sscanf(p, "%d", &cnt) > 0) { |
| - | dot = find_line(cnt); |
| - | dot_skip_over_ws(); |
| - | return; |
| + | cnt = 0; |
| + | stop = end_line(stop); |
| + | while (start <= stop && start <= end - 1) { |
| + | start = end_line(start); |
| + | if (*start == '\n') |
| + | cnt++; |
| + | start++; |
| } | |
| - | not_implemented(p); |
| - | #else |
| + | return cnt; |
| + | } |
| - | char c, *buf1, *q, *r; |
| - | char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN]; |
| - | int i, l, li, b, e; |
| - | int useforce; |
| - | # if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC |
| - | char *orig_buf; |
| - | # endif |
| + | static char *find_line(int li) // find beginning of line #li |
| + | { |
| + | char *q; |
| - | // :3154 // if (-e line 3154) goto it else stay put |
| - | // :4,33w! foo // write a portion of buffer to file "foo" |
| - | // :w // write all of buffer to current file |
| - | // :q // quit |
| - | // :q! // quit- dont care about modified file |
| - | // :'a,'z!sort -u // filter block through sort |
| - | // :'f // goto mark "f" |
| - | // :'fl // list literal the mark "f" line |
| - | // :.r bar // read file "bar" into buffer before dot |
| - | // :/123/,/abc/d // delete lines from "123" line to "abc" line |
| - | // :/xyz/ // goto the "xyz" line |
| - | // :s/find/replace/ // substitute pattern "find" with "replace" |
| - | // :!<cmd> // run <cmd> then return |
| - | // |
| + | for (q = text; li > 1; li--) { |
| + | q = next_line(q); |
| + | } |
| + | return q; |
| + | } |
| - | if (!buf[0]) |
| - | goto ret; |
| - | if (*buf == ':') |
| - | buf++; // move past the ':' |
| + | static int next_tabstop(int col) |
| + | { |
| + | return col + ((tabstop - 1) - (col % tabstop)); |
| + | } |
| - | li = i = 0; |
| - | b = e = -1; |
| - | q = text; // assume 1,$ for the range |
| - | r = end - 1; |
| - | li = count_lines(text, end - 1); |
| - | fn = current_filename; |
| + | //----- Erase the Screen[] memory ------------------------------ |
| + | static void screen_erase(void) |
| + | { |
| + | memset(screen, ' ', screensize); // clear new screen |
| + | } |
| - | // look for optional address(es) :. :1 :1,9 :'q,'a :% |
| - | buf = get_address(buf, &b, &e); |
| + | //----- Synchronize the cursor to Dot -------------------------- |
| + | static NOINLINE void sync_cursor(char *d, int *row, int *col) |
| + | { |
| + | char *beg_cur; // begin and end of "d" line |
| + | char *tp; |
| + | int cnt, ro, co; |
| - | # if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC |
| - | // remember orig command line |
| - | orig_buf = buf; |
| - | # endif |
| + | beg_cur = begin_line(d); // first char of cur line |
| - | // get the COMMAND into cmd[] |
| - | buf1 = cmd; |
| - | while (*buf != '\0') { |
| - | if (isspace(*buf)) |
| - | break; |
| - | *buf1++ = *buf++; |
| + | if (beg_cur < screenbegin) { |
| + | // "d" is before top line on screen |
| + | // how many lines do we have to move |
| + | cnt = count_lines(beg_cur, screenbegin); |
| + | sc1: |
| + | screenbegin = beg_cur; |
| + | if (cnt > (rows - 1) / 2) { |
| + | // we moved too many lines. put "dot" in middle of screen |
| + | for (cnt = 0; cnt < (rows - 1) / 2; cnt++) { |
| + | screenbegin = prev_line(screenbegin); |
| + | } |
| + | } |
| + | } else { |
| + | char *end_scr; // begin and end of screen |
| + | end_scr = end_screen(); // last char of screen |
| + | if (beg_cur > end_scr) { |
| + | // "d" is after bottom line on screen |
| + | // how many lines do we have to move |
| + | cnt = count_lines(end_scr, beg_cur); |
| + | if (cnt > (rows - 1) / 2) |
| + | goto sc1; // too many lines |
| + | for (ro = 0; ro < cnt - 1; ro++) { |
| + | // move screen begin the same amount |
| + | screenbegin = next_line(screenbegin); |
| + | // now, move the end of screen |
| + | end_scr = next_line(end_scr); |
| + | end_scr = end_line(end_scr); |
| + | } |
| + | } |
| } | |
| - | *buf1 = '\0'; |
| - | // get any ARGuments |
| - | while (isblank(*buf)) |
| - | buf++; |
| - | strcpy(args, buf); |
| - | useforce = FALSE; |
| - | buf1 = last_char_is(cmd, '!'); |
| - | if (buf1) { |
| - | useforce = TRUE; |
| - | *buf1 = '\0'; // get rid of ! |
| + | // "d" is on screen- find out which row |
| + | tp = screenbegin; |
| + | for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row |
| + | if (tp == beg_cur) |
| + | break; |
| + | tp = next_line(tp); |
| } | |
| - | if (b >= 0) { |
| - | // if there is only one addr, then the addr |
| - | // is the line number of the single line the |
| - | // user wants. So, reset the end |
| - | // pointer to point at end of the "b" line |
| - | q = find_line(b); // what line is #b |
| - | r = end_line(q); |
| - | li = 1; |
| + | |
| + | // find out what col "d" is on |
| + | co = 0; |
| + | while (tp < d) { // drive "co" to correct column |
| + | if (*tp == '\n') //vda || *tp == '\0') |
| + | break; |
| + | if (*tp == '\t') { |
| + | // handle tabs like real vi |
| + | if (d == tp && cmd_mode) { |
| + | break; |
| + | } |
| + | co = next_tabstop(co); |
| + | } else if ((unsigned char)*tp < ' ' || *tp == 0x7f) { |
| + | co++; // display as ^X, use 2 columns |
| + | } |
| + | co++; |
| + | tp++; |
| } | |
| - | if (e >= 0) { |
| - | // we were given two addrs. change the |
| - | // end pointer to the addr given by user. |
| - | r = find_line(e); // what line is #e |
| - | r = end_line(r); |
| - | li = e - b + 1; |
| + | |
| + | // "co" is the column where "dot" is. |
| + | // The screen has "columns" columns. |
| + | // The currently displayed columns are 0+offset -- columns+ofset |
| + | // |-------------------------------------------------------------| |
| + | // ^ ^ ^ |
| + | // offset | |------- columns ----------------| |
| + | // |
| + | // If "co" is already in this range then we do not have to adjust offset |
| + | // but, we do have to subtract the "offset" bias from "co". |
| + | // If "co" is outside this range then we have to change "offset". |
| + | // If the first char of a line is a tab the cursor will try to stay |
| + | // in column 7, but we have to set offset to 0. |
| + | |
| + | if (co < 0 + offset) { |
| + | offset = co; |
| } | |
| - | // ------------ now look for the command ------------ |
| - | i = strlen(cmd); |
| - | if (i == 0) { // :123CR goto line #123 |
| - | if (b >= 0) { |
| - | dot = find_line(b); // what line is #b |
| - | dot_skip_over_ws(); |
| - | } |
| + | if (co >= columns + offset) { |
| + | offset = co - columns + 1; |
| } | |
| - | # if ENABLE_FEATURE_ALLOW_EXEC |
| - | else if (cmd[0] == '!') { // run a cmd |
| - | int retcode; |
| - | // :!ls run the <cmd> |
| - | go_bottom_and_clear_to_eol(); |
| - | cookmode(); |
| - | retcode = system(orig_buf + 1); // run the cmd |
| - | if (retcode) |
| - | printf("\nshell returned %i\n\n", retcode); |
| - | rawmode(); |
| - | Hit_Return(); // let user see results |
| + | // if the first char of the line is a tab, and "dot" is sitting on it |
| + | // force offset to 0. |
| + | if (d == beg_cur && *d == '\t') { |
| + | offset = 0; |
| } | |
| - | # endif |
| - | else if (cmd[0] == '=' && !cmd[1]) { // where is the address |
| - | if (b < 0) { // no addr given- use defaults |
| - | b = e = count_lines(text, dot); |
| - | } |
| - | status_line("%d", b); |
| - | } else if (strncmp(cmd, "delete", i) == 0) { // delete lines |
| - | if (b < 0) { // no addr given- use defaults |
| - | q = begin_line(dot); // assume .,. for the range |
| - | r = end_line(dot); |
| - | } |
| - | dot = yank_delete(q, r, 1, YANKDEL, ALLOW_UNDO); // save, then delete lines |
| - | dot_skip_over_ws(); |
| - | } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file |
| - | int size; |
| + | co -= offset; |
| - | // don't edit, if the current file has been modified |
| - | if (modified_count && !useforce) { |
| - | status_line_bold("No write since last change (:%s! overrides)", cmd); |
| - | goto ret; |
| - | } |
| - | if (args[0]) { |
| - | // the user supplied a file name |
| - | fn = args; |
| - | } else if (current_filename && current_filename[0]) { |
| - | // no user supplied name- use the current filename |
| - | // fn = current_filename; was set by default |
| - | } else { |
| - | // no user file name, no current name- punt |
| - | status_line_bold("No current filename"); |
| - | goto ret; |
| - | } |
| + | *row = ro; |
| + | *col = co; |
| + | } |
| - | size = init_text_buffer(fn); |
| + | //----- The Colon commands ------------------------------------- |
| + | #if ENABLE_FEATURE_VI_COLON |
| + | static char *get_one_address(char *p, int *addr) // get colon addr, if present |
| + | { |
| + | int st; |
| + | char *q; |
| + | IF_FEATURE_VI_YANKMARK(char c;) |
| + | IF_FEATURE_VI_SEARCH(char *pat;) |
| - | # if ENABLE_FEATURE_VI_YANKMARK |
| - | if (Ureg >= 0 && Ureg < 28) { |
| - | free(reg[Ureg]); // free orig line reg- for 'U' |
| - | reg[Ureg] = NULL; |
| - | } |
| - | if (YDreg >= 0 && YDreg < 28) { |
| - | free(reg[YDreg]); // free default yank/delete register |
| - | reg[YDreg] = NULL; |
| - | } |
| - | # endif |
| - | // how many lines in text[]? |
| - | li = count_lines(text, end - 1); |
| - | status_line("'%s'%s" |
| - | IF_FEATURE_VI_READONLY("%s") |
| - | " %dL, %dC", |
| - | current_filename, |
| - | (size < 0 ? " [New file]" : ""), |
| - | IF_FEATURE_VI_READONLY( |
| - | ((readonly_mode) ? " [Readonly]" : ""), |
| - | ) |
| - | li, (int)(end - text) |
| - | ); |
| - | } else if (strncmp(cmd, "file", i) == 0) { // what File is this |
| - | if (b != -1 || e != -1) { |
| - | status_line_bold("No address allowed on this command"); |
| - | goto ret; |
| - | } |
| - | if (args[0]) { |
| - | // user wants a new filename |
| - | free(current_filename); |
| - | current_filename = xstrdup(args); |
| - | } else { |
| - | // user wants file status info |
| - | last_status_cksum = 0; // force status update |
| + | *addr = -1; // assume no addr |
| + | if (*p == '.') { // the current line |
| + | p++; |
| + | q = begin_line(dot); |
| + | *addr = count_lines(text, q); |
| + | } |
| + | #if ENABLE_FEATURE_VI_YANKMARK |
| + | else if (*p == '\'') { // is this a mark addr |
| + | p++; |
| + | c = tolower(*p); |
| + | p++; |
| + | if (c >= 'a' && c <= 'z') { |
| + | // we have a mark |
| + | c = c - 'a'; |
| + | q = mark[(unsigned char) c]; |
| + | if (q != NULL) { // is mark valid |
| + | *addr = count_lines(text, q); |
| + | } |
| } | |
| - | } else if (strncmp(cmd, "features", i) == 0) { // what features are available |
| - | // print out values of all features |
| - | go_bottom_and_clear_to_eol(); |
| - | cookmode(); |
| - | show_help(); |
| - | rawmode(); |
| - | Hit_Return(); |
| - | } else if (strncmp(cmd, "list", i) == 0) { // literal print line |
| - | if (b < 0) { // no addr given- use defaults |
| - | q = begin_line(dot); // assume .,. for the range |
| - | r = end_line(dot); |
| + | } |
| + | #endif |
| + | #if ENABLE_FEATURE_VI_SEARCH |
| + | else if (*p == '/') { // a search pattern |
| + | q = strchrnul(++p, '/'); |
| + | pat = xstrndup(p, q - p); // save copy of pattern |
| + | p = q; |
| + | if (*p == '/') |
| + | p++; |
| + | q = char_search(dot, pat, (FORWARD << 1) | FULL); |
| + | if (q != NULL) { |
| + | *addr = count_lines(text, q); |
| } | |
| - | go_bottom_and_clear_to_eol(); |
| - | puts("\r"); |
| - | for (; q <= r; q++) { |
| - | int c_is_no_print; |
| + | free(pat); |
| + | } |
| + | #endif |
| + | else if (*p == '$') { // the last line in file |
| + | p++; |
| + | q = begin_line(end - 1); |
| + | *addr = count_lines(text, q); |
| + | } else if (isdigit(*p)) { // specific line number |
| + | sscanf(p, "%d%n", addr, &st); |
| + | p += st; |
| + | } else { |
| + | // unrecognized address - assume -1 |
| + | *addr = -1; |
| + | } |
| + | return p; |
| + | } |
| - | c = *q; |
| - | c_is_no_print = (c & 0x80) && !Isprint(c); |
| - | if (c_is_no_print) { |
| - | c = '.'; |
| - | standout_start(); |
| - | } |
| - | if (c == '\n') { |
| - | write1("$\r"); |
| - | } else if (c < ' ' || c == 127) { |
| - | bb_putchar('^'); |
| - | if (c == 127) |
| - | c = '?'; |
| - | else |
| - | c += '@'; |
| - | } |
| - | bb_putchar(c); |
| - | if (c_is_no_print) |
| - | standout_end(); |
| - | } |
| - | Hit_Return(); |
| - | } else if (strncmp(cmd, "quit", i) == 0 // quit |
| - | || strncmp(cmd, "next", i) == 0 // edit next file |
| - | || strncmp(cmd, "prev", i) == 0 // edit previous file |
| + | static char *get_address(char *p, int *b, int *e) // get two colon addrs, if present |
| + | { |
| + | //----- get the address' i.e., 1,3 'a,'b ----- |
| + | // get FIRST addr, if present |
| + | while (isblank(*p)) |
| + | p++; // skip over leading spaces |
| + | if (*p == '%') { // alias for 1,$ |
| + | p++; |
| + | *b = 1; |
| + | *e = count_lines(text, end-1); |
| + | goto ga0; |
| + | } |
| + | p = get_one_address(p, b); |
| + | while (isblank(*p)) |
| + | p++; |
| + | if (*p == ',') { // is there a address separator |
| + | p++; |
| + | while (isblank(*p)) |
| + | p++; |
| + | // get SECOND addr, if present |
| + | p = get_one_address(p, e); |
| + | } |
| + | ga0: |
| + | while (isblank(*p)) |
| + | p++; // skip over trailing spaces |
| + | return p; |
| + | } |
| + | |
| + | #if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS |
| + | static void setops(const char *args, const char *opname, int flg_no, |
| + | const char *short_opname, int opt) |
| + | { |
| + | const char *a = args + flg_no; |
| + | int l = strlen(opname) - 1; // opname have + ' ' |
| + | |
| + | // maybe strncmp? we had tons of erroneous strncasecmp's... |
| + | if (strncasecmp(a, opname, l) == 0 |
| + | || strncasecmp(a, short_opname, 2) == 0 |
| ) { | |
| - | int n; |
| - | if (useforce) { |
| - | if (*cmd == 'q') { |
| - | // force end of argv list |
| - | optind = save_argc; |
| - | } |
| + | if (flg_no) |
| + | vi_setops &= ~opt; |
| + | else |
| + | vi_setops |= opt; |
| + | } |
| + | } |
| + | #endif |
| + | |
| + | #endif /* FEATURE_VI_COLON */ |
| + | |
| + | // buf must be no longer than MAX_INPUT_LEN! |
| + | static void colon(char *buf) |
| + | { |
| + | #if !ENABLE_FEATURE_VI_COLON |
| + | // Simple ":cmd" handler with minimal set of commands |
| + | char *p = buf; |
| + | int cnt; |
| + | |
| + | if (*p == ':') |
| + | p++; |
| + | cnt = strlen(p); |
| + | if (cnt == 0) |
| + | return; |
| + | if (strncmp(p, "quit", cnt) == 0 |
| + | || strncmp(p, "q!", cnt) == 0 |
| + | ) { |
| + | if (modified_count && p[1] != '!') { |
| + | status_line_bold("No write since last change (:%s! overrides)", p); |
| + | } else { |
| editing = 0; | |
| - | goto ret; |
| - | } |
| - | // don't exit if the file been modified |
| - | if (modified_count) { |
| - | status_line_bold("No write since last change (:%s! overrides)", cmd); |
| - | goto ret; |
| - | } |
| - | // are there other file to edit |
| - | n = save_argc - optind - 1; |
| - | if (*cmd == 'q' && n > 0) { |
| - | status_line_bold("%d more file(s) to edit", n); |
| - | goto ret; |
| } | |
| - | if (*cmd == 'n' && n <= 0) { |
| - | status_line_bold("No more files to edit"); |
| - | goto ret; |
| + | return; |
| + | } |
| + | if (strncmp(p, "write", cnt) == 0 |
| + | || strncmp(p, "wq", cnt) == 0 |
| + | || strncmp(p, "wn", cnt) == 0 |
| + | || (p[0] == 'x' && !p[1]) |
| + | ) { |
| + | if (modified_count != 0 || p[0] != 'x') { |
| + | cnt = file_write(current_filename, text, end - 1); |
| } | |
| - | if (*cmd == 'p') { |
| - | // are there previous files to edit |
| - | if (optind < 1) { |
| - | status_line_bold("No previous files to edit"); |
| - | goto ret; |
| + | if (cnt < 0) { |
| + | if (cnt == -1) |
| + | status_line_bold("Write error: "STRERROR_FMT STRERROR_ERRNO); |
| + | } else { |
| + | modified_count = 0; |
| + | last_modified_count = -1; |
| + | status_line("'%s' %dL, %dC", |
| + | current_filename, |
| + | count_lines(text, end - 1), cnt |
| + | ); |
| + | if (p[0] == 'x' |
| + | || p[1] == 'q' || p[1] == 'n' |
| + | || p[1] == 'Q' || p[1] == 'N' |
| + | ) { |
| + | editing = 0; |
| } | |
| - | optind -= 2; |
| } | |
| - | editing = 0; |
| - | } else if (strncmp(cmd, "read", i) == 0) { // read file into text[] |
| - | int size; |
| + | return; |
| + | } |
| + | if (strncmp(p, "file", cnt) == 0) { |
| + | last_status_cksum = 0; // force status update |
| + | return; |
| + | } |
| + | if (sscanf(p, "%d", &cnt) > 0) { |
| + | dot = find_line(cnt); |
| + | dot_skip_over_ws(); |
| + | return; |
| + | } |
| + | not_implemented(p); |
| + | #else |
| - | fn = args; |
| - | if (!fn[0]) { |
| - | status_line_bold("No filename given"); |
| - | goto ret; |
| - | } |
| + | char c, *buf1, *q, *r; |
| + | char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN]; |
| + | int i, l, li, b, e; |
| + | int useforce; |
| + | # if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC |
| + | char *orig_buf; |
| + | # endif |
| + | |
| + | // :3154 // if (-e line 3154) goto it else stay put |
| + | // :4,33w! foo // write a portion of buffer to file "foo" |
| + | // :w // write all of buffer to current file |
| + | // :q // quit |
| + | // :q! // quit- dont care about modified file |
| + | // :'a,'z!sort -u // filter block through sort |
| + | // :'f // goto mark "f" |
| + | // :'fl // list literal the mark "f" line |
| + | // :.r bar // read file "bar" into buffer before dot |
| + | // :/123/,/abc/d // delete lines from "123" line to "abc" line |
| + | // :/xyz/ // goto the "xyz" line |
| + | // :s/find/replace/ // substitute pattern "find" with "replace" |
| + | // :!<cmd> // run <cmd> then return |
| + | // |
| + | |
| + | if (!buf[0]) |
| + | goto ret; |
| + | if (*buf == ':') |
| + | buf++; // move past the ':' |
| + | |
| + | li = i = 0; |
| + | b = e = -1; |
| + | q = text; // assume 1,$ for the range |
| + | r = end - 1; |
| + | li = count_lines(text, end - 1); |
| + | fn = current_filename; |
| + | |
| + | // look for optional address(es) :. :1 :1,9 :'q,'a :% |
| + | buf = get_address(buf, &b, &e); |
| + | |
| + | # if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC |
| + | // remember orig command line |
| + | orig_buf = buf; |
| + | # endif |
| + | |
| + | // get the COMMAND into cmd[] |
| + | buf1 = cmd; |
| + | while (*buf != '\0') { |
| + | if (isspace(*buf)) |
| + | break; |
| + | *buf1++ = *buf++; |
| + | } |
| + | *buf1 = '\0'; |
| + | // get any ARGuments |
| + | while (isblank(*buf)) |
| + | buf++; |
| + | strcpy(args, buf); |
| + | useforce = FALSE; |
| + | buf1 = last_char_is(cmd, '!'); |
| + | if (buf1) { |
| + | useforce = TRUE; |
| + | *buf1 = '\0'; // get rid of ! |
| + | } |
| + | if (b >= 0) { |
| + | // if there is only one addr, then the addr |
| + | // is the line number of the single line the |
| + | // user wants. So, reset the end |
| + | // pointer to point at end of the "b" line |
| + | q = find_line(b); // what line is #b |
| + | r = end_line(q); |
| + | li = 1; |
| + | } |
| + | if (e >= 0) { |
| + | // we were given two addrs. change the |
| + | // end pointer to the addr given by user. |
| + | r = find_line(e); // what line is #e |
| + | r = end_line(r); |
| + | li = e - b + 1; |
| + | } |
| + | // ------------ now look for the command ------------ |
| + | i = strlen(cmd); |
| + | if (i == 0) { // :123CR goto line #123 |
| + | if (b >= 0) { |
| + | dot = find_line(b); // what line is #b |
| + | dot_skip_over_ws(); |
| + | } |
| + | } |
| + | # if ENABLE_FEATURE_ALLOW_EXEC |
| + | else if (cmd[0] == '!') { // run a cmd |
| + | int retcode; |
| + | // :!ls run the <cmd> |
| + | go_bottom_and_clear_to_eol(); |
| + | cookmode(); |
| + | retcode = system(orig_buf + 1); // run the cmd |
| + | if (retcode) |
| + | printf("\nshell returned %i\n\n", retcode); |
| + | rawmode(); |
| + | Hit_Return(); // let user see results |
| + | } |
| + | # endif |
| + | else if (cmd[0] == '=' && !cmd[1]) { // where is the address |
| + | if (b < 0) { // no addr given- use defaults |
| + | b = e = count_lines(text, dot); |
| + | } |
| + | status_line("%d", b); |
| + | } else if (strncmp(cmd, "delete", i) == 0) { // delete lines |
| + | if (b < 0) { // no addr given- use defaults |
| + | q = begin_line(dot); // assume .,. for the range |
| + | r = end_line(dot); |
| + | } |
| + | dot = yank_delete(q, r, 1, YANKDEL, ALLOW_UNDO); // save, then delete lines |
| + | dot_skip_over_ws(); |
| + | } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file |
| + | int size; |
| + | |
| + | // don't edit, if the current file has been modified |
| + | if (modified_count && !useforce) { |
| + | status_line_bold("No write since last change (:%s! overrides)", cmd); |
| + | goto ret; |
| + | } |
| + | if (args[0]) { |
| + | // the user supplied a file name |
| + | fn = args; |
| + | } else if (current_filename && current_filename[0]) { |
| + | // no user supplied name- use the current filename |
| + | // fn = current_filename; was set by default |
| + | } else { |
| + | // no user file name, no current name- punt |
| + | status_line_bold("No current filename"); |
| + | goto ret; |
| + | } |
| + | |
| + | size = init_text_buffer(fn); |
| + | |
| + | # if ENABLE_FEATURE_VI_YANKMARK |
| + | if (Ureg >= 0 && Ureg < 28) { |
| + | free(reg[Ureg]); // free orig line reg- for 'U' |
| + | reg[Ureg] = NULL; |
| + | } |
| + | if (YDreg >= 0 && YDreg < 28) { |
| + | free(reg[YDreg]); // free default yank/delete register |
| + | reg[YDreg] = NULL; |
| + | } |
| + | # endif |
| + | // how many lines in text[]? |
| + | li = count_lines(text, end - 1); |
| + | status_line("'%s'%s" |
| + | IF_FEATURE_VI_READONLY("%s") |
| + | " %dL, %dC", |
| + | current_filename, |
| + | (size < 0 ? " [New file]" : ""), |
| + | IF_FEATURE_VI_READONLY( |
| + | ((readonly_mode) ? " [Readonly]" : ""), |
| + | ) |
| + | li, (int)(end - text) |
| + | ); |
| + | } else if (strncmp(cmd, "file", i) == 0) { // what File is this |
| + | if (b != -1 || e != -1) { |
| + | status_line_bold("No address allowed on this command"); |
| + | goto ret; |
| + | } |
| + | if (args[0]) { |
| + | // user wants a new filename |
| + | free(current_filename); |
| + | current_filename = xstrdup(args); |
| + | } else { |
| + | // user wants file status info |
| + | last_status_cksum = 0; // force status update |
| + | } |
| + | } else if (strncmp(cmd, "features", i) == 0) { // what features are available |
| + | // print out values of all features |
| + | go_bottom_and_clear_to_eol(); |
| + | cookmode(); |
| + | show_help(); |
| + | rawmode(); |
| + | Hit_Return(); |
| + | } else if (strncmp(cmd, "list", i) == 0) { // literal print line |
| + | if (b < 0) { // no addr given- use defaults |
| + | q = begin_line(dot); // assume .,. for the range |
| + | r = end_line(dot); |
| + | } |
| + | go_bottom_and_clear_to_eol(); |
| + | puts("\r"); |
| + | for (; q <= r; q++) { |
| + | int c_is_no_print; |
| + | |
| + | c = *q; |
| + | c_is_no_print = (c & 0x80) && !Isprint(c); |
| + | if (c_is_no_print) { |
| + | c = '.'; |
| + | standout_start(); |
| + | } |
| + | if (c == '\n') { |
| + | write1("$\r"); |
| + | } else if (c < ' ' || c == 127) { |
| + | bb_putchar('^'); |
| + | if (c == 127) |
| + | c = '?'; |
| + | else |
| + | c += '@'; |
| + | } |
| + | bb_putchar(c); |
| + | if (c_is_no_print) |
| + | standout_end(); |
| + | } |
| + | Hit_Return(); |
| + | } else if (strncmp(cmd, "quit", i) == 0 // quit |
| + | || strncmp(cmd, "next", i) == 0 // edit next file |
| + | || strncmp(cmd, "prev", i) == 0 // edit previous file |
| + | ) { |
| + | int n; |
| + | if (useforce) { |
| + | if (*cmd == 'q') { |
| + | // force end of argv list |
| + | optind = save_argc; |
| + | } |
| + | editing = 0; |
| + | goto ret; |
| + | } |
| + | // don't exit if the file been modified |
| + | if (modified_count) { |
| + | status_line_bold("No write since last change (:%s! overrides)", cmd); |
| + | goto ret; |
| + | } |
| + | // are there other file to edit |
| + | n = save_argc - optind - 1; |
| + | if (*cmd == 'q' && n > 0) { |
| + | status_line_bold("%d more file(s) to edit", n); |
| + | goto ret; |
| + | } |
| + | if (*cmd == 'n' && n <= 0) { |
| + | status_line_bold("No more files to edit"); |
| + | goto ret; |
| + | } |
| + | if (*cmd == 'p') { |
| + | // are there previous files to edit |
| + | if (optind < 1) { |
| + | status_line_bold("No previous files to edit"); |
| + | goto ret; |
| + | } |
| + | optind -= 2; |
| + | } |
| + | editing = 0; |
| + | } else if (strncmp(cmd, "read", i) == 0) { // read file into text[] |
| + | int size; |
| + | |
| + | fn = args; |
| + | if (!fn[0]) { |
| + | status_line_bold("No filename given"); |
| + | goto ret; |
| + | } |
| if (b < 0) { // no addr given- use defaults | |
| q = begin_line(dot); // assume "dot" | |
| } | |
| @@ -1205,348 +1481,154 @@ static void colon(char *buf) | |
| int dont_chain_first_item = ALLOW_UNDO; | |
| # endif | |
| - | // F points to the "find" pattern |
| - | // R points to the "replace" pattern |
| - | // replace the cmd line delimiters "/" with NULs |
| - | c = orig_buf[1]; // what is the delimiter |
| - | F = orig_buf + 2; // start of "find" |
| - | R = strchr(F, c); // middle delimiter |
| - | if (!R) |
| - | goto colon_s_fail; |
| - | len_F = R - F; |
| - | *R++ = '\0'; // terminate "find" |
| - | flags = strchr(R, c); |
| - | if (!flags) |
| - | goto colon_s_fail; |
| - | len_R = flags - R; |
| - | *flags++ = '\0'; // terminate "replace" |
| - | gflag = *flags; |
| - | |
| - | q = begin_line(q); |
| - | if (b < 0) { // maybe :s/foo/bar/ |
| - | q = begin_line(dot); // start with cur line |
| - | b = count_lines(text, q); // cur line number |
| - | } |
| - | if (e < 0) |
| - | e = b; // maybe :.s/foo/bar/ |
| - | |
| - | for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0 |
| - | char *ls = q; // orig line start |
| - | char *found; |
| - | vc4: |
| - | found = char_search(q, F, (FORWARD << 1) | LIMITED); // search cur line only for "find" |
| - | if (found) { |
| - | uintptr_t bias; |
| - | // we found the "find" pattern - delete it |
| - | // For undo support, the first item should not be chained |
| - | text_hole_delete(found, found + len_F - 1, dont_chain_first_item); |
| - | # if ENABLE_FEATURE_VI_UNDO |
| - | dont_chain_first_item = ALLOW_UNDO_CHAIN; |
| - | # endif |
| - | // insert the "replace" patern |
| - | bias = string_insert(found, R, ALLOW_UNDO_CHAIN); |
| - | found += bias; |
| - | ls += bias; |
| - | /*q += bias; - recalculated anyway */ |
| - | // check for "global" :s/foo/bar/g |
| - | if (gflag == 'g') { |
| - | if ((found + len_R) < end_line(ls)) { |
| - | q = found + len_R; |
| - | goto vc4; // don't let q move past cur line |
| - | } |
| - | } |
| - | } |
| - | q = next_line(ls); |
| - | } |
| - | # endif /* FEATURE_VI_SEARCH */ |
| - | } else if (strncmp(cmd, "version", i) == 0) { // show software version |
| - | status_line(BB_VER); |
| - | } else if (strncmp(cmd, "write", i) == 0 // write text to file |
| - | || strncmp(cmd, "wq", i) == 0 |
| - | || strncmp(cmd, "wn", i) == 0 |
| - | || (cmd[0] == 'x' && !cmd[1]) |
| - | ) { |
| - | int size; |
| - | //int forced = FALSE; |
| - | |
| - | // is there a file name to write to? |
| - | if (args[0]) { |
| - | fn = args; |
| - | } |
| - | # if ENABLE_FEATURE_VI_READONLY |
| - | if (readonly_mode && !useforce) { |
| - | status_line_bold("'%s' is read only", fn); |
| - | goto ret; |
| - | } |
| - | # endif |
| - | //if (useforce) { |
| - | // if "fn" is not write-able, chmod u+w |
| - | // sprintf(syscmd, "chmod u+w %s", fn); |
| - | // system(syscmd); |
| - | // forced = TRUE; |
| - | //} |
| - | if (modified_count != 0 || cmd[0] != 'x') { |
| - | size = r - q + 1; |
| - | l = file_write(fn, q, r); |
| - | } else { |
| - | size = 0; |
| - | l = 0; |
| - | } |
| - | //if (useforce && forced) { |
| - | // chmod u-w |
| - | // sprintf(syscmd, "chmod u-w %s", fn); |
| - | // system(syscmd); |
| - | // forced = FALSE; |
| - | //} |
| - | if (l < 0) { |
| - | if (l == -1) |
| - | status_line_bold_errno(fn); |
| - | } else { |
| - | // how many lines written |
| - | li = count_lines(q, q + l - 1); |
| - | status_line("'%s' %dL, %dC", fn, li, l); |
| - | if (l == size) { |
| - | if (q == text && q + l == end) { |
| - | modified_count = 0; |
| - | last_modified_count = -1; |
| - | } |
| - | if (cmd[0] == 'x' |
| - | || cmd[1] == 'q' || cmd[1] == 'n' |
| - | || cmd[1] == 'Q' || cmd[1] == 'N' |
| - | ) { |
| - | editing = 0; |
| - | } |
| - | } |
| - | } |
| - | # if ENABLE_FEATURE_VI_YANKMARK |
| - | } else if (strncmp(cmd, "yank", i) == 0) { // yank lines |
| - | if (b < 0) { // no addr given- use defaults |
| - | q = begin_line(dot); // assume .,. for the range |
| - | r = end_line(dot); |
| - | } |
| - | text_yank(q, r, YDreg); |
| - | li = count_lines(q, r); |
| - | status_line("Yank %d lines (%d chars) into [%c]", |
| - | li, strlen(reg[YDreg]), what_reg()); |
| - | # endif |
| - | } else { |
| - | // cmd unknown |
| - | not_implemented(cmd); |
| - | } |
| - | ret: |
| - | dot = bound_dot(dot); // make sure "dot" is valid |
| - | return; |
| - | # if ENABLE_FEATURE_VI_SEARCH |
| - | colon_s_fail: |
| - | status_line(":s expression missing delimiters"); |
| - | # endif |
| - | #endif /* FEATURE_VI_COLON */ |
| - | } |
| - | |
| - | static void Hit_Return(void) |
| - | { |
| - | int c; |
| - | |
| - | standout_start(); |
| - | write1("[Hit return to continue]"); |
| - | standout_end(); |
| - | while ((c = get_one_char()) != '\n' && c != '\r') |
| - | continue; |
| - | redraw(TRUE); // force redraw all |
| - | } |
| - | |
| - | static int next_tabstop(int col) |
| - | { |
| - | return col + ((tabstop - 1) - (col % tabstop)); |
| - | } |
| - | |
| - | //----- Synchronize the cursor to Dot -------------------------- |
| - | static NOINLINE void sync_cursor(char *d, int *row, int *col) |
| - | { |
| - | char *beg_cur; // begin and end of "d" line |
| - | char *tp; |
| - | int cnt, ro, co; |
| - | |
| - | beg_cur = begin_line(d); // first char of cur line |
| - | |
| - | if (beg_cur < screenbegin) { |
| - | // "d" is before top line on screen |
| - | // how many lines do we have to move |
| - | cnt = count_lines(beg_cur, screenbegin); |
| - | sc1: |
| - | screenbegin = beg_cur; |
| - | if (cnt > (rows - 1) / 2) { |
| - | // we moved too many lines. put "dot" in middle of screen |
| - | for (cnt = 0; cnt < (rows - 1) / 2; cnt++) { |
| - | screenbegin = prev_line(screenbegin); |
| - | } |
| - | } |
| - | } else { |
| - | char *end_scr; // begin and end of screen |
| - | end_scr = end_screen(); // last char of screen |
| - | if (beg_cur > end_scr) { |
| - | // "d" is after bottom line on screen |
| - | // how many lines do we have to move |
| - | cnt = count_lines(end_scr, beg_cur); |
| - | if (cnt > (rows - 1) / 2) |
| - | goto sc1; // too many lines |
| - | for (ro = 0; ro < cnt - 1; ro++) { |
| - | // move screen begin the same amount |
| - | screenbegin = next_line(screenbegin); |
| - | // now, move the end of screen |
| - | end_scr = next_line(end_scr); |
| - | end_scr = end_line(end_scr); |
| - | } |
| - | } |
| - | } |
| - | // "d" is on screen- find out which row |
| - | tp = screenbegin; |
| - | for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row |
| - | if (tp == beg_cur) |
| - | break; |
| - | tp = next_line(tp); |
| - | } |
| - | |
| - | // find out what col "d" is on |
| - | co = 0; |
| - | while (tp < d) { // drive "co" to correct column |
| - | if (*tp == '\n') //vda || *tp == '\0') |
| - | break; |
| - | if (*tp == '\t') { |
| - | // handle tabs like real vi |
| - | if (d == tp && cmd_mode) { |
| - | break; |
| - | } |
| - | co = next_tabstop(co); |
| - | } else if ((unsigned char)*tp < ' ' || *tp == 0x7f) { |
| - | co++; // display as ^X, use 2 columns |
| - | } |
| - | co++; |
| - | tp++; |
| - | } |
| - | |
| - | // "co" is the column where "dot" is. |
| - | // The screen has "columns" columns. |
| - | // The currently displayed columns are 0+offset -- columns+ofset |
| - | // |-------------------------------------------------------------| |
| - | // ^ ^ ^ |
| - | // offset | |------- columns ----------------| |
| - | // |
| - | // If "co" is already in this range then we do not have to adjust offset |
| - | // but, we do have to subtract the "offset" bias from "co". |
| - | // If "co" is outside this range then we have to change "offset". |
| - | // If the first char of a line is a tab the cursor will try to stay |
| - | // in column 7, but we have to set offset to 0. |
| - | |
| - | if (co < 0 + offset) { |
| - | offset = co; |
| - | } |
| - | if (co >= columns + offset) { |
| - | offset = co - columns + 1; |
| - | } |
| - | // if the first char of the line is a tab, and "dot" is sitting on it |
| - | // force offset to 0. |
| - | if (d == beg_cur && *d == '\t') { |
| - | offset = 0; |
| - | } |
| - | co -= offset; |
| - | |
| - | *row = ro; |
| - | *col = co; |
| - | } |
| - | |
| - | //----- Text Movement Routines --------------------------------- |
| - | static char *begin_line(char *p) // return pointer to first char cur line |
| - | { |
| - | if (p > text) { |
| - | p = memrchr(text, '\n', p - text); |
| - | if (!p) |
| - | return text; |
| - | return p + 1; |
| - | } |
| - | return p; |
| - | } |
| - | |
| - | static char *end_line(char *p) // return pointer to NL of cur line |
| - | { |
| - | if (p < end - 1) { |
| - | p = memchr(p, '\n', end - p - 1); |
| - | if (!p) |
| - | return end - 1; |
| - | } |
| - | return p; |
| - | } |
| - | |
| - | static char *dollar_line(char *p) // return pointer to just before NL line |
| - | { |
| - | p = end_line(p); |
| - | // Try to stay off of the Newline |
| - | if (*p == '\n' && (p - begin_line(p)) > 0) |
| - | p--; |
| - | return p; |
| - | } |
| - | |
| - | static char *prev_line(char *p) // return pointer first char prev line |
| - | { |
| - | p = begin_line(p); // goto beginning of cur line |
| - | if (p > text && p[-1] == '\n') |
| - | p--; // step to prev line |
| - | p = begin_line(p); // goto beginning of prev line |
| - | return p; |
| - | } |
| - | |
| - | static char *next_line(char *p) // return pointer first char next line |
| - | { |
| - | p = end_line(p); |
| - | if (p < end - 1 && *p == '\n') |
| - | p++; // step to next line |
| - | return p; |
| - | } |
| - | |
| - | //----- Text Information Routines ------------------------------ |
| - | static char *end_screen(void) |
| - | { |
| - | char *q; |
| - | int cnt; |
| + | // F points to the "find" pattern |
| + | // R points to the "replace" pattern |
| + | // replace the cmd line delimiters "/" with NULs |
| + | c = orig_buf[1]; // what is the delimiter |
| + | F = orig_buf + 2; // start of "find" |
| + | R = strchr(F, c); // middle delimiter |
| + | if (!R) |
| + | goto colon_s_fail; |
| + | len_F = R - F; |
| + | *R++ = '\0'; // terminate "find" |
| + | flags = strchr(R, c); |
| + | if (!flags) |
| + | goto colon_s_fail; |
| + | len_R = flags - R; |
| + | *flags++ = '\0'; // terminate "replace" |
| + | gflag = *flags; |
| - | // find new bottom line |
| - | q = screenbegin; |
| - | for (cnt = 0; cnt < rows - 2; cnt++) |
| - | q = next_line(q); |
| - | q = end_line(q); |
| - | return q; |
| - | } |
| + | q = begin_line(q); |
| + | if (b < 0) { // maybe :s/foo/bar/ |
| + | q = begin_line(dot); // start with cur line |
| + | b = count_lines(text, q); // cur line number |
| + | } |
| + | if (e < 0) |
| + | e = b; // maybe :.s/foo/bar/ |
| - | // count line from start to stop |
| - | static int count_lines(char *start, char *stop) |
| - | { |
| - | char *q; |
| - | int cnt; |
| + | for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0 |
| + | char *ls = q; // orig line start |
| + | char *found; |
| + | vc4: |
| + | found = char_search(q, F, (FORWARD << 1) | LIMITED); // search cur line only for "find" |
| + | if (found) { |
| + | uintptr_t bias; |
| + | // we found the "find" pattern - delete it |
| + | // For undo support, the first item should not be chained |
| + | text_hole_delete(found, found + len_F - 1, dont_chain_first_item); |
| + | # if ENABLE_FEATURE_VI_UNDO |
| + | dont_chain_first_item = ALLOW_UNDO_CHAIN; |
| + | # endif |
| + | // insert the "replace" patern |
| + | bias = string_insert(found, R, ALLOW_UNDO_CHAIN); |
| + | found += bias; |
| + | ls += bias; |
| + | /*q += bias; - recalculated anyway */ |
| + | // check for "global" :s/foo/bar/g |
| + | if (gflag == 'g') { |
| + | if ((found + len_R) < end_line(ls)) { |
| + | q = found + len_R; |
| + | goto vc4; // don't let q move past cur line |
| + | } |
| + | } |
| + | } |
| + | q = next_line(ls); |
| + | } |
| + | # endif /* FEATURE_VI_SEARCH */ |
| + | } else if (strncmp(cmd, "version", i) == 0) { // show software version |
| + | status_line(BB_VER); |
| + | } else if (strncmp(cmd, "write", i) == 0 // write text to file |
| + | || strncmp(cmd, "wq", i) == 0 |
| + | || strncmp(cmd, "wn", i) == 0 |
| + | || (cmd[0] == 'x' && !cmd[1]) |
| + | ) { |
| + | int size; |
| + | //int forced = FALSE; |
| - | if (stop < start) { // start and stop are backwards- reverse them |
| - | q = start; |
| - | start = stop; |
| - | stop = q; |
| - | } |
| - | cnt = 0; |
| - | stop = end_line(stop); |
| - | while (start <= stop && start <= end - 1) { |
| - | start = end_line(start); |
| - | if (*start == '\n') |
| - | cnt++; |
| - | start++; |
| + | // is there a file name to write to? |
| + | if (args[0]) { |
| + | fn = args; |
| + | } |
| + | # if ENABLE_FEATURE_VI_READONLY |
| + | if (readonly_mode && !useforce) { |
| + | status_line_bold("'%s' is read only", fn); |
| + | goto ret; |
| + | } |
| + | # endif |
| + | //if (useforce) { |
| + | // if "fn" is not write-able, chmod u+w |
| + | // sprintf(syscmd, "chmod u+w %s", fn); |
| + | // system(syscmd); |
| + | // forced = TRUE; |
| + | //} |
| + | if (modified_count != 0 || cmd[0] != 'x') { |
| + | size = r - q + 1; |
| + | l = file_write(fn, q, r); |
| + | } else { |
| + | size = 0; |
| + | l = 0; |
| + | } |
| + | //if (useforce && forced) { |
| + | // chmod u-w |
| + | // sprintf(syscmd, "chmod u-w %s", fn); |
| + | // system(syscmd); |
| + | // forced = FALSE; |
| + | //} |
| + | if (l < 0) { |
| + | if (l == -1) |
| + | status_line_bold_errno(fn); |
| + | } else { |
| + | // how many lines written |
| + | li = count_lines(q, q + l - 1); |
| + | status_line("'%s' %dL, %dC", fn, li, l); |
| + | if (l == size) { |
| + | if (q == text && q + l == end) { |
| + | modified_count = 0; |
| + | last_modified_count = -1; |
| + | } |
| + | if (cmd[0] == 'x' |
| + | || cmd[1] == 'q' || cmd[1] == 'n' |
| + | || cmd[1] == 'Q' || cmd[1] == 'N' |
| + | ) { |
| + | editing = 0; |
| + | } |
| + | } |
| + | } |
| + | # if ENABLE_FEATURE_VI_YANKMARK |
| + | } else if (strncmp(cmd, "yank", i) == 0) { // yank lines |
| + | if (b < 0) { // no addr given- use defaults |
| + | q = begin_line(dot); // assume .,. for the range |
| + | r = end_line(dot); |
| + | } |
| + | text_yank(q, r, YDreg); |
| + | li = count_lines(q, r); |
| + | status_line("Yank %d lines (%d chars) into [%c]", |
| + | li, strlen(reg[YDreg]), what_reg()); |
| + | # endif |
| + | } else { |
| + | // cmd unknown |
| + | not_implemented(cmd); |
| } | |
| - | return cnt; |
| + | ret: |
| + | dot = bound_dot(dot); // make sure "dot" is valid |
| + | return; |
| + | # if ENABLE_FEATURE_VI_SEARCH |
| + | colon_s_fail: |
| + | status_line(":s expression missing delimiters"); |
| + | # endif |
| + | #endif /* FEATURE_VI_COLON */ |
| } | |
| - | static char *find_line(int li) // find beginning of line #li |
| + | static void Hit_Return(void) |
| { | |
| - | char *q; |
| + | int c; |
| - | for (q = text; li > 1; li--) { |
| - | q = next_line(q); |
| - | } |
| - | return q; |
| + | standout_start(); |
| + | write1("[Hit return to continue]"); |
| + | standout_end(); |
| + | while ((c = get_one_char()) != '\n' && c != '\r') |
| + | continue; |
| + | redraw(TRUE); // force redraw all |
| } | |
| //----- Dot Movement Routines ---------------------------------- | |
| @@ -2430,32 +2512,6 @@ static char *swap_context(char *p) // goto new context for '' command make this | |
| } | |
| #endif /* FEATURE_VI_YANKMARK */ | |
| - | //----- Set terminal attributes -------------------------------- |
| - | static void rawmode(void) |
| - | { |
| - | // no TERMIOS_CLEAR_ISIG: leave ISIG on - allow signals |
| - | set_termios_to_raw(STDIN_FILENO, &term_orig, TERMIOS_RAW_CRNL); |
| - | erase_char = term_orig.c_cc[VERASE]; |
| - | } |
| - | |
| - | static void cookmode(void) |
| - | { |
| - | fflush_all(); |
| - | tcsetattr_stdin_TCSANOW(&term_orig); |
| - | } |
| - | |
| - | static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready |
| - | { |
| - | struct pollfd pfd[1]; |
| - | |
| - | if (hund != 0) |
| - | fflush_all(); |
| - | |
| - | pfd[0].fd = STDIN_FILENO; |
| - | pfd[0].events = POLLIN; |
| - | return safe_poll(pfd, 1, hund*10) > 0; |
| - | } |
| - | |
| //----- IO Routines -------------------------------------------- | |
| static int readit(void) // read (maybe cursor) key from stdin | |
| { | |
| @@ -2637,55 +2693,6 @@ static int file_write(char *fn, char *first, char *last) | |
| return charcnt; | |
| } | |
| - | //----- Terminal Drawing --------------------------------------- |
| - | // The terminal is made up of 'rows' line of 'columns' columns. |
| - | // classically this would be 24 x 80. |
| - | // screen coordinates |
| - | // 0,0 ... 0,79 |
| - | // 1,0 ... 1,79 |
| - | // . ... . |
| - | // . ... . |
| - | // 22,0 ... 22,79 |
| - | // 23,0 ... 23,79 <- status line |
| - | |
| - | //----- Move the cursor to row x col (count from 0, not 1) ------- |
| - | static void place_cursor(int row, int col) |
| - | { |
| - | char cm1[sizeof(ESC_SET_CURSOR_POS) + sizeof(int)*3 * 2]; |
| - | |
| - | if (row < 0) row = 0; |
| - | if (row >= rows) row = rows - 1; |
| - | if (col < 0) col = 0; |
| - | if (col >= columns) col = columns - 1; |
| - | |
| - | sprintf(cm1, ESC_SET_CURSOR_POS, row + 1, col + 1); |
| - | write1(cm1); |
| - | } |
| - | |
| - | //----- Erase from cursor to end of line ----------------------- |
| - | static void clear_to_eol(void) |
| - | { |
| - | write1(ESC_CLEAR2EOL); |
| - | } |
| - | |
| - | static void go_bottom_and_clear_to_eol(void) |
| - | { |
| - | place_cursor(rows - 1, 0); |
| - | clear_to_eol(); |
| - | } |
| - | |
| - | //----- Start standout mode ------------------------------------ |
| - | static void standout_start(void) |
| - | { |
| - | write1(ESC_BOLD_TEXT); |
| - | } |
| - | |
| - | //----- End standout mode -------------------------------------- |
| - | static void standout_end(void) |
| - | { |
| - | write1(ESC_NORM_TEXT); |
| - | } |
| - | |
| //----- Flash the screen -------------------------------------- | |
| static void flash(int h) | |
| { | |
| @@ -2709,13 +2716,6 @@ static void indicate_error(void) | |
| } | |
| } | |
| - | //----- Screen[] Routines -------------------------------------- |
| - | //----- Erase the Screen[] memory ------------------------------ |
| - | static void screen_erase(void) |
| - | { |
| - | memset(screen, ' ', screensize); // clear new screen |
| - | } |
| - | |
| static int bufsum(char *buf, int count) | |
| { | |
| int sum = 0; | |