action parse_command_line(const unsigned char * command_line, int * const num_arg, unsigned char ** const string_arg, const int exec_only_options) { int h; action a; const unsigned char *p = command_line; if (num_arg) *num_arg = -1; if (string_arg) *string_arg = NULL; if (!command_line) return NOP_A; if (!*p) return NOP_A; while(isasciispace(*p)) p++; command_line = p; if (!isalpha(*p)) return NOP_A; while(*p && !isasciispace(*p)) p++; h = hash_cmd(command_line, p - command_line); if ((a = hash_table[h]) && !cmdcmp(commands[--a].name, command_line) || (a = short_hash_table[h]) && !cmdcmp(commands[--a].short_name, command_line)) { while(isasciispace(*p)) p++; if (!(*p && (commands[a].flags & NO_ARGS))) { if (!*p || (commands[a].flags & ARG_IS_STRING) || isxdigit(*p) || *p == 'x' || *p =='X') { if ((commands[a].flags & IS_OPTION) || !exec_only_options) { if (*p) { if ((commands[a].flags & ARG_IS_STRING) && string_arg) { int len = strlen(p); if (len > 1 && *p == '"' && p[len - 1] == '"') { p++; len -= 2; } if (len == 0 && !(commands[a].flags & EMPTY_STRING_OK)) return -STRING_IS_EMPTY; if (!(*string_arg = malloc(len + 1))) return -OUT_OF_MEMORY; memcpy(*string_arg, p, len); (*string_arg)[len] = 0; } else if (num_arg) { char *q; *num_arg = strtol(p, &q, 0); if (*q && !isasciispace(*q)) return -NOT_A_NUMBER; } } return a; } D(fprintf(stderr,"parse_command error: Can execute only options.\n");) return -CAN_EXECUTE_ONLY_OPTIONS; } D(fprintf(stderr,"parse_command error: Has numeric argument.\n");) return -HAS_NUMERIC_ARGUMENT; }
int center(buffer * const b) { line_desc * const ld = b->cur_line_desc; const int right_margin = b->opt.right_margin ? b->opt.right_margin : ne_columns; int64_t len, start_pos = 0, end_pos = ld->line_len; while(start_pos < ld->line_len && isasciispace(ld->line[start_pos])) start_pos = next_pos(ld->line, start_pos, b->encoding); if (start_pos == ld->line_len) return OK; while(isasciispace(ld->line[prev_pos(ld->line, end_pos, b->encoding)])) end_pos = prev_pos(ld->line, end_pos, b->encoding); len = b->encoding == ENC_UTF8 ? utf8strlen(&ld->line[start_pos], end_pos - start_pos) : end_pos - start_pos; if (len >= right_margin) return OK; b->cur_pos = -1; start_undo_chain(b); delete_stream(b, ld, b->cur_line, end_pos, ld->line_len - end_pos); delete_stream(b, ld, b->cur_line, 0, start_pos); insert_spaces(b, ld, b->cur_line, 0, (right_margin - len) / 2); end_undo_chain(b); return OK; }
static int save_space(line_desc * const ld, const int tab_size, const encoding_type encoding) { if (pa_space) free(pa_space); pa_space = NULL; pa_space_len = 0; pa_space_pos = 0; if (!ld->line) return 0; /* No data on this line. */ int64_t pos = 0; while(pos < ld->line_len && isasciispace(ld->line[pos])) pos = next_pos(ld->line, pos, encoding); if (pos == ld->line_len) return 0; /* Blank lines don't count. */ pa_space_pos = pos; pa_space_len = calc_width(ld, pos, tab_size, encoding); if (pos == 0) { return 1; } if ((pa_space = malloc(pos))) { memcpy(pa_space, ld->line, pos); return 1; } return 0; }
int cmdcmp(const unsigned char *c, const unsigned char *m) { assert(c != NULL); assert(m != NULL); while (*c && ascii_up_case[*c] == ascii_up_case[*m]) { c++; m++; } return *c || *m && !isasciispace(*m) ; }
static int is_part_of_paragraph(const line_desc * const ld, const int tab_size, int64_t * const first_non_blank, const encoding_type encoding) { *first_non_blank = -1; if (!ld->line) return 0; int64_t pos = 0; while (pos < ld->line_len && isasciispace(ld->line[pos])) pos = next_pos(ld->line, pos, encoding); if (pos < ld->line_len && calc_width(ld, pos, tab_size, encoding) == pa_space_len) { *first_non_blank = pos; return 1; } return 0; }
int shift(buffer * const b, char *p, char *msg, int msg_size) { const bool use_tabs = b->opt.tabs && b->opt.shift_tabs; const int64_t init_line = b->cur_line, init_pos = b->cur_pos, init_y = b->cur_y; line_desc *ld = NULL, *start_line_desc = NULL; int64_t shift_size = 1; char dir = '>'; int shift_mag = b->opt.tab_size, rc = 0; /* Parse parm p; looks like [<|>] ### [s|t], but we allow them in any order, once, with optional white space. */ if (p) { int dir_b = 0, size_b = 0, st_b = 0; while (*p) { if (isasciispace(*p)) p++; else if (!dir_b && (dir_b = (*p == '<' || *p == '>'))) dir = *p++; else if (!size_b && (size_b = isdigit((unsigned char)*p))) { errno = 0; shift_size = strtoll(p, &p, 10); if (errno) return INVALID_SHIFT_SPECIFIED; } else if (!st_b && (st_b = (*p == 's' || *p == 'S'))) { shift_mag = 1; p++; } else if (!st_b && (st_b = (*p == 't' || *p == 'T'))) p++; else return INVALID_SHIFT_SPECIFIED; } } shift_size *= max(1, shift_mag); if (shift_size == 0) return INVALID_SHIFT_SPECIFIED; int64_t first_line = b->cur_line, last_line = b->cur_line, left_col = 0; if (b->marking) { if (b->mark_is_vertical) left_col = min(calc_width(b->cur_line_desc, b->block_start_pos, b->opt.tab_size, b->encoding), calc_width(b->cur_line_desc, b->cur_pos, b->opt.tab_size, b->encoding)); first_line = min(b->block_start_line, b->cur_line); last_line = max(b->block_start_line, b->cur_line); } /* If we're shifting left (dir=='<'), verify that we have sufficient white space to remove on all the relevant lines before making any changes, i. */ if (dir == '<') { shift_size = -shift_size; /* signed shift_size now also indicates direction. */ for (int64_t line = first_line; !rc && line <= last_line; line++) { int64_t pos; goto_line(b, line); pos = calc_pos(b->cur_line_desc, left_col, b->opt.tab_size, b->encoding); while (pos < b->cur_line_desc->line_len && left_col - calc_width(b->cur_line_desc, pos, b->opt.tab_size, b->encoding) > shift_size) { if (isasciispace(b->cur_line_desc->line[pos])) pos = next_pos(b->cur_line_desc->line, pos, b->encoding); else { rc = INSUFFICIENT_WHITESPACE; break; } } } } if (!rc) { start_undo_chain(b); for (int64_t line = first_line; line <= last_line; line++) { int64_t pos, c_pos, c_col_orig, offset; b->attr_len = -1; goto_line(b, line); b->cur_pos = -1; ld = b->cur_line_desc; if (line == first_line) start_line_desc = ld; pos = calc_pos(ld, left_col, b->opt.tab_size, b->encoding); /* If left_col is in the middle of a tab, pos will be on that tab. */ /* whitespace adjustment strategy: 1. Starting from left_col, advance to the right to the first non-blank character C. 2. Note C's col. The desired new column is this value +/- shift_size. 3. Move left looking for the first tab or non-whitespace or the left_col, whichever comes first. Whitespace changes all take place at that transition point. 4. While C's col is wrong if C's col is too far to the right, if we're on a space, delete it; else if there's a tab to our left, delete it; else we should not have started, because it's not possible! if C's col is too far to the left, if its needs to be beyond the next tab stop, insert a tab and move right; else insert a space. */ /* 1. */ while (pos < ld->line_len && isasciispace(ld->line[pos])) pos = next_pos(ld->line, pos, b->encoding); if (pos >= ld->line_len) continue; /* We ran off the end of the line. */ /* line[pos] should be the first non-blank character. */ /* 2. */ c_pos = pos; c_col_orig = calc_width(ld, c_pos, b->opt.tab_size, b->encoding); /* 3. */ while (pos && ld->line[pos-1] == ' ') pos = prev_pos(ld->line, pos, b->encoding); /* If pos is non-zero, it should be on a blank, with only blanks between here and c_pos. */ /* 4. */ /* offset = how_far_we_have_moved - how_far_we_want_to_move. */ while (!stop && (offset = calc_width(ld, c_pos, b->opt.tab_size, b->encoding)-c_col_orig - shift_size)) { if (offset > 0) { /* still too far right; remove whitespace */ if (ld->line[pos] == ' ') { delete_stream(b, ld, b->cur_line, pos, 1); c_pos--; } else if (pos) { /* should be a tab just to our left */ pos = prev_pos(ld->line, pos, b->encoding); /* now we're on the tab */ if (ld->line[pos] == '\t') { delete_stream(b, ld, b->cur_line, pos, 1); c_pos--; } else break; /* Should have been a tab. This should never happen! Give up on this line and go mangle the next one. */ } else break; /* This should never happen; give up on this line and go mangle the next one. */ } else if (offset < 0) { /* too far left; insert whitespace */ char c = ' '; if (use_tabs && (b->opt.tab_size - calc_width(ld, pos, b->opt.tab_size, b->encoding) % b->opt.tab_size) <= -offset ) c = '\t'; if (insert_one_char(b, ld, b->cur_line, pos, c)) { break; } pos++; c_pos++; } } } end_undo_chain(b); if (b->syn) { b->attr_len = -1; need_attr_update = true; update_syntax_states(b, -1, start_line_desc, (line_desc *)ld->ld_node.next); } update_window_lines(b, b->top_line_desc, 0, ne_lines - 2, false); } /* put the screen back where way we found it. */ goto_line_pos(b, init_line, init_pos); delay_update(); const int64_t avshift = b->cur_y - init_y; if (avshift) { snprintf(msg, msg_size, "%c%" PRId64, avshift > 0 ? 'T' :'B', avshift > 0 ? avshift : -avshift); adjust_view(b, msg); } return rc; }
int paragraph(buffer * const b) { line_desc *ld = b->cur_line_desc, *start_line_desc = ld; if (!ld->line) return line_down(b); /* Establish appropriate leading space. This will be taken from the line following the current line if it is non-blank. Otherwise it will be taken from the current line. Save a copy of it for later as space[]. **/ if ( !( (ld->ld_node.next->next && save_space((line_desc *)ld->ld_node.next, b->opt.tab_size, b->encoding)) || save_space(ld, b->opt.tab_size, b->encoding) ) ) return line_down(b); int64_t pos = b->cur_pos; b->cur_pos = -1; start_undo_chain(b); /* This insertion and deletion of a single character ensures that the cursor ends up here after an undo. */ int64_t line = b->cur_line; insert_one_char(b, ld, line, 0, ' '); delete_stream(b, ld, line, 0, 1); const int right_margin = b->opt.right_margin ? b->opt.right_margin : ne_columns; bool done; do { done = true; /* set this to false if we do any work in the loop. */ trim_trailing_space(b, ld, line, b->encoding); /* Suck up subsequent lines until this one is long enough to need splitting */ while ((calc_width(ld, ld->line_len, b->opt.tab_size, b->encoding) <= right_margin) && ld->ld_node.next->next && is_part_of_paragraph((line_desc *)ld->ld_node.next, b->opt.tab_size, &pos, b->encoding)) { line_desc *ld_next = (line_desc *)ld->ld_node.next; insert_one_char(b, ld, line, ld->line_len, ' '); if (pos) delete_stream(b, ld_next, line + 1, 0, pos); /* pos was set by is_part_of_paragraph() above. */ delete_stream(b, ld, line, ld->line_len, 1); /* joins next line to this one */ trim_trailing_space(b, ld, line, b->encoding); done = false; } if (calc_width(ld, ld->line_len, b->opt.tab_size, b->encoding) > right_margin) { int64_t spaces; int64_t split_pos; /* Skip past leading spaces... */ pos = 0; while(pos < ld->line_len && isasciispace(ld->line[pos])) pos = next_pos(ld->line, pos, b->encoding); /* Find the split point */ split_pos = spaces = 0; while (pos < ld->line_len && (calc_width(ld, pos, b->opt.tab_size, b->encoding) < right_margin || ! split_pos)) { if (isasciispace(ld->line[pos])) { split_pos = pos; spaces = 0; while (pos < ld->line_len && isasciispace(ld->line[pos])) { pos = next_pos(ld->line, pos, b->encoding); spaces++; } } else pos = next_pos(ld->line, pos, b->encoding); } if (split_pos) { done = false; /* Remove any space at the split point. */ if (spaces) delete_stream(b, ld, line, split_pos, spaces); /* Split the line at the split point. (We are done with this line) */ insert_one_line(b, ld, line, split_pos); /* Make the new next line the current line **/ if (ld->ld_node.next->next) { ld = (line_desc *)ld->ld_node.next; line++; if (pa_space && pa_space_len && pa_space_pos) insert_stream(b, ld, line, 0, pa_space, pa_space_pos); trim_trailing_space(b, ld, line, b->encoding); } } else { /* Line not split; is there a next one in the paragraph? */ if ( ld->ld_node.next->next && is_part_of_paragraph((line_desc *)ld->ld_node.next, b->opt.tab_size, &pos, b->encoding) ) { ld = (line_desc *)ld->ld_node.next; line++; done = false; } } } } while (!stop && !done); end_undo_chain(b); if (pa_space) { free(pa_space); pa_space = NULL; } if (b->syn) { b->attr_len = -1; need_attr_update = true; update_syntax_states(b, -1, start_line_desc, (line_desc *)ld->ld_node.next); } update_window_lines(b, b->cur_line_desc, b->cur_y, ne_lines - 2, false); goto_line_pos(b, line, pos); if (stop || line_down(b) == ERROR) return stop ? STOPPED : ERROR; /* Try to find the first non-blank starting with this line. */ ld = b->cur_line_desc; line = b->cur_line; do { if (ld->line) { for (pos = 0; pos < ld->line_len; pos = next_pos(ld->line, pos, b->encoding)) { if (!isasciispace(ld->line[pos])) { goto_line_pos(b, line, pos); return ld->ld_node.next ? OK : ERROR; } } } ld = (line_desc *)ld->ld_node.next; line++; } while (ld->ld_node.next); return b->cur_line_desc->ld_node.next ? OK : ERROR; }
static void trim_trailing_space(buffer * const b, line_desc *ld, const int64_t line, const encoding_type encoding) { if (!ld->line) return; int64_t pos = ld->line_len; while (pos > 0 && isasciispace(ld->line[pos - 1])) pos = prev_pos(ld->line, pos, encoding); if (pos >= 0 && pos < ld->line_len) delete_stream(b, ld, line, pos, ld->line_len - pos); }