Point line_end_position (int count) { Point pt = line_beginning_position (count); pt.o = astr_len (get_line_text (pt.p)); return pt; }
static int find_substr (astr as, const char *s2, size_t s2size, size_t from, size_t to, bool forward, bool notbol, bool noteol, bool regex, bool icase) { int ret = -1; struct re_pattern_buffer pattern; struct re_registers search_regs; reg_syntax_t syntax = RE_SYNTAX_EMACS; memset (&pattern, 0, sizeof (pattern)); if (!regex) syntax |= RE_PLAIN; if (icase) syntax |= RE_ICASE; re_set_syntax (syntax); search_regs.num_regs = 1; re_find_err = re_compile_pattern (s2, (int) s2size, &pattern); pattern.not_bol = notbol; pattern.not_eol = noteol; if (!re_find_err) ret = re_search (&pattern, astr_cstr (as), (int) astr_len (as), forward ? from : to - 1, forward ? (to - from) : -(to - 1 - from), &search_regs); if (ret >= 0) { ret = forward ? search_regs.end[0] : ret; free (search_regs.start); free (search_regs.end); } regfree (&pattern); return ret; }
static bool search (Point pt, const char *s, int forward, int regexp) { Line *lp = pt.p; astr as = get_line_text (lp); size_t ssize = strlen (s), from = 0, to = astr_len (as); bool downcase = get_variable_bool ("case-fold-search") && no_upper (s, ssize, regexp); bool notbol = false, noteol = false; int pos; if (ssize < 1) return false; /* Match first line. */ if (forward) { notbol = pt.o > from; from = pt.o; } else { noteol = pt.o < to; to = pt.o; } pos = find_substr (as, s, ssize, from, to, forward, notbol, noteol, regexp, downcase); /* Match following lines. */ while (pos < 0) { lp = (forward ? get_line_next : get_line_prev) (lp); if (lp == get_buffer_lines (cur_bp)) break; as = get_line_text (lp); pos = find_substr (as, s, ssize, 0, astr_len (as), forward, false, false, regexp, downcase); } if (pos < 0) return false; while (get_buffer_pt (cur_bp).p != lp) (forward ? next_line : previous_line) (); pt = get_buffer_pt (cur_bp); pt.o = pos; set_buffer_pt (cur_bp, pt); thisflag |= FLAG_NEED_RESYNC; return true; }
Point point_max(void) { Point pt; pt.p = list_prev(cur_bp->lines); pt.n = cur_bp->num_lines; pt.o = astr_len(list_prev(cur_bp->lines)->item); return pt; }
Point point_max (void) { Point pt; pt.p = get_line_prev (get_buffer_lines (cur_bp)); pt.n = get_buffer_last_line (cur_bp); pt.o = astr_len (get_line_text (get_line_prev (get_buffer_lines (cur_bp)))); return pt; }
static void draw_line(size_t line, size_t startcol, Window *wp, Line *lp, size_t lineno, Region *r, int highlight) { size_t x, i; term_move(line, 0); for (x = 0, i = startcol; i < astr_len(lp->item) && x < wp->ewidth; i++) { if (highlight && in_region(lineno, i, r)) outch(*astr_char(lp->item, (ptrdiff_t)i), FONT_REVERSE, &x); else outch(*astr_char(lp->item, (ptrdiff_t)i), FONT_NORMAL, &x); } draw_end_of_line(line, wp, lineno, r, highlight, x, i); }
int point_dist(Point pt1, Point pt2) { int size = 0; Line *lp; if (cmp_point(pt1, pt2) > 0) swap_point(&pt1, &pt2); for (lp = pt1.p; ; lp = list_next(lp)) { size += astr_len(lp->item); if (lp == pt1.p) size -= pt1.o; if (lp == pt2.p) { size -= astr_len(lp->item) - pt2.o; break; } else size++; } return size; }
static void draw_status_line(size_t line, Window *wp) { size_t i; char *buf, *eol_type; Point pt = window_pt(wp); astr as, bs; term_attrset(1, FONT_REVERSE); term_move(line, 0); for (i = 0; i < wp->ewidth; ++i) term_addch('-'); if (cur_bp->eol == coding_eol_cr) eol_type = "(Mac)"; else if (cur_bp->eol == coding_eol_crlf) eol_type = "(DOS)"; else eol_type = ":"; term_move(line, 0); bs = astr_afmt(astr_new(), "(%d,%d)", pt.n+1, get_goalc_wp(wp)); as = astr_afmt(astr_new(), "--%s%2s %-15s %s %-9s (Text", eol_type, make_mode_line_flags(wp), wp->bp->name, make_screen_pos(wp, &buf), astr_cstr(bs)); free(buf); astr_delete(bs); if (wp->bp->flags & BFLAG_AUTOFILL) astr_cat_cstr(as, " Fill"); if (wp->bp->flags & BFLAG_OVERWRITE) astr_cat_cstr(as, " Ovwrt"); if (thisflag & FLAG_DEFINING_MACRO) astr_cat_cstr(as, " Def"); if (wp->bp->flags & BFLAG_ISEARCH) astr_cat_cstr(as, " Isearch"); astr_cat_char(as, ')'); term_addnstr(astr_cstr(as), min(term_width(), astr_len(as))); astr_delete(as); term_attrset(1, FONT_NORMAL); }
/* set up the appropriate watch on the directory indicated by sfilename */ static int setup_inotify_watch(int notify_fd, struct astr *sfilename) { char dirbuf[astr_len(sfilename) + 1]; char *dir; int dwatch; strcpy(dirbuf, astr_cstr(sfilename)); dir = dirname(dirbuf); dwatch = inotify_add_watch(notify_fd, dir, IN_CLOSE_WRITE|IN_MOVED_TO); if(dwatch <= 0) { fprintf(stderr, ERRORTEXT("Fatal: failed to add inotify watch for %s") ": %s\n", dir, strerror(errno)); exit(EXIT_FAILURE); } return dwatch; }
/* * Copy a region of text into an allocated buffer. */ char * copy_text_block (size_t startn, size_t starto, size_t size) { char *buf, *dp; size_t max_size, n, i; Line *lp; max_size = 10; dp = buf = (char *) xzalloc (max_size); lp = cur_bp->pt.p; n = cur_bp->pt.n; if (n > startn) do lp = lp->prev; while (--n > startn); else if (n < startn) do lp = lp->next; while (++n < startn); for (i = starto; dp - buf < (int) size;) { if (dp >= buf + max_size) { int save_off = dp - buf; max_size += 10; buf = (char *) xrealloc (buf, max_size); dp = buf + save_off; } if (i < astr_len (lp->text)) *dp++ = *astr_char (lp->text, (ptrdiff_t) (i++)); else { *dp++ = '\n'; lp = lp->next; i = 0; } } return buf; }
/* * Return the current directory. */ static astr get_current_dir(void) { astr buf; int p; if (cur_bp->filename != NULL) /* If the current buffer has a filename, get the current directory name from it. */ buf = astr_new_cstr(cur_bp->filename); else { /* Get the current directory name from the system. */ buf = agetcwd(); if (astr_len(buf) != 0 && *astr_char(buf, -1) != '/') astr_cat_char(buf, '/'); } p = astr_rfind_cstr(buf, "/"); astr_truncate(buf, p + 1); return buf; }
/* * Return a `~/foo' like path if the user is under his home directory, * else the unmodified path. */ astr compact_path (astr path) { struct passwd *pw = getpwuid (getuid ()); if (pw != NULL) { /* Replace `/userhome/' (if found) with `~/'. */ size_t homelen = strlen (pw->pw_dir); if (homelen > 0 && pw->pw_dir[homelen - 1] == '/') homelen--; if (astr_len (path) > homelen && !strncmp (pw->pw_dir, astr_cstr (path), homelen) && astr_get (path, homelen) == '/') astr_cpy (path, astr_cat_cstr (astr_new_cstr ("~/"), astr_cstr (path) + homelen + 1)); } return path; }
static int kill_line(int literally) { if (!eolp()) { if (warn_if_readonly_buffer()) return FALSE; undo_save(UNDO_INSERT_BLOCK, cur_bp->pt, astr_len(cur_bp->pt.p->item) - cur_bp->pt.o, 0); undo_nosave = TRUE; while (!eolp()) { kill_ring_push(following_char()); FUNCALL(delete_char); } undo_nosave = FALSE; thisflag |= FLAG_DONE_KILL; if (!literally) return TRUE; } if (list_next(cur_bp->pt.p) != cur_bp->lines) { if (!FUNCALL(delete_char)) return FALSE; kill_ring_push('\n'); thisflag |= FLAG_DONE_KILL; return TRUE; } minibuf_error("End of buffer"); return FALSE; }
Point line_end_position(int count) { Point pt = line_beginning_position(count); pt.o = astr_len(pt.p->item); return pt; }
/* * This functions does some corrections and expansions to * the passed path: * * - expands `~/' and `~name/' expressions; * - replaces `//' with `/' (restarting from the root directory); * - removes `..' and `.' entries. * * The return value indicates success or failure. */ bool expand_path (astr path) { int ok = true; const char *sp = astr_cstr (path); astr epath = astr_new (); if (*sp != '/' && *sp != '~') { astr_cat (epath, agetcwd ()); if (astr_len (epath) == 0 || astr_get (epath, astr_len (epath) - 1) != '/') astr_cat_char (epath, '/'); } for (const char *p = sp; *p != '\0';) { if (*p == '/') { if (*++p == '/') { /* Got `//'. Restart from this point. */ while (*p == '/') p++; astr_truncate (epath, 0); } if (astr_len (epath) == 0 || astr_get (epath, astr_len (epath) - 1) != '/') astr_cat_char (epath, '/'); } else if (*p == '~' && (p == sp || p[-1] == '/')) { /* Got `/~' or leading `~'. Restart from this point. */ struct passwd *pw; astr_truncate (epath, 0); ++p; if (*p == '/') { /* Got `~/'. Insert the user's home directory. */ pw = getpwuid (getuid ()); if (pw == NULL) { ok = false; break; } if (!STREQ (pw->pw_dir, "/")) astr_cat_cstr (epath, pw->pw_dir); } else { /* Got `~something'. Insert that user's home directory. */ astr as = astr_new (); while (*p != '\0' && *p != '/') astr_cat_char (as, *p++); pw = getpwnam (astr_cstr (as)); if (pw == NULL) { ok = false; break; } astr_cat_cstr (epath, pw->pw_dir); } } else if (*p == '.' && (p[1] == '/' || p[1] == '\0')) { /* Got `.'. */ ++p; } else if (*p == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\0')) { /* Got `..'. */ if (astr_len (epath) >= 1 && astr_get (epath, astr_len (epath) - 1) == '/') astr_truncate (epath, astr_len (epath) - 1); while (astr_get (epath, astr_len (epath) - 1) != '/' && astr_len (epath) >= 1) astr_truncate (epath, astr_len (epath) - 1); p += 2; } if (*p != '~') while (*p != '\0' && *p != '/') astr_cat_char (epath, *p++); } astr_cpy (path, epath); return ok; }
static astr do_minibuf_read (const char *prompt, const char *value, size_t pos, Completion * cp, History * hp) { static int overwrite_mode = 0; int c, thistab, lasttab = -1; size_t prompt_len; char *s; astr as = astr_new_cstr (value), saved = NULL; prompt_len = strlen (prompt); if (pos == SIZE_MAX) pos = astr_len (as); for (;;) { switch (lasttab) { case COMPLETION_MATCHEDNONUNIQUE: s = " [Complete, but not unique]"; break; case COMPLETION_NOTMATCHED: s = " [No match]"; break; case COMPLETION_MATCHED: s = " [Sole completion]"; break; default: s = ""; } draw_minibuf_read (prompt, astr_cstr (as), prompt_len, s, pos); thistab = -1; switch (c = getkey ()) { case KBD_NOKEY: break; case KBD_CTRL | 'z': FUNCALL (suspend_emacs); break; case KBD_RET: term_move (term_height () - 1, 0); term_clrtoeol (); if (saved) astr_delete (saved); return as; case KBD_CANCEL: term_move (term_height () - 1, 0); term_clrtoeol (); if (saved) astr_delete (saved); astr_delete (as); return NULL; case KBD_CTRL | 'a': case KBD_HOME: pos = 0; break; case KBD_CTRL | 'e': case KBD_END: pos = astr_len (as); break; case KBD_CTRL | 'b': case KBD_LEFT: if (pos > 0) --pos; else ding (); break; case KBD_CTRL | 'f': case KBD_RIGHT: if (pos < astr_len (as)) ++pos; else ding (); break; case KBD_CTRL | 'k': /* FIXME: do kill-register save. */ if (pos < astr_len (as)) astr_truncate (as, pos); else ding (); break; case KBD_BS: if (pos > 0) astr_remove (as, --pos, 1); else ding (); break; case KBD_CTRL | 'd': case KBD_DEL: if (pos < astr_len (as)) astr_remove (as, pos, 1); else ding (); break; case KBD_INS: overwrite_mode = overwrite_mode ? 0 : 1; break; case KBD_META | 'v': case KBD_PGUP: if (cp == NULL) { ding (); break; } if (get_completion_flags (cp) & CFLAG_POPPEDUP) { completion_scroll_down (); thistab = lasttab; } break; case KBD_CTRL | 'v': case KBD_PGDN: if (cp == NULL) { ding (); break; } if (get_completion_flags (cp) & CFLAG_POPPEDUP) { completion_scroll_up (); thistab = lasttab; } break; case KBD_UP: case KBD_META | 'p': if (hp) { const char *elem = previous_history_element (hp); if (elem) { if (!saved) saved = astr_cpy (astr_new (), as); astr_cpy_cstr (as, elem); } } break; case KBD_DOWN: case KBD_META | 'n': if (hp) { const char *elem = next_history_element (hp); if (elem) astr_cpy_cstr (as, elem); else if (saved) { astr_cpy (as, saved); astr_delete (saved); saved = NULL; } } break; case KBD_TAB: got_tab: if (cp == NULL) { ding (); break; } if (lasttab != -1 && lasttab != COMPLETION_NOTMATCHED && get_completion_flags (cp) & CFLAG_POPPEDUP) { completion_scroll_up (); thistab = lasttab; } else { astr bs = astr_new (); astr_cpy (bs, as); thistab = completion_try (cp, bs, true); astr_delete (bs); switch (thistab) { case COMPLETION_MATCHED: case COMPLETION_MATCHEDNONUNIQUE: case COMPLETION_NONUNIQUE: { bs = astr_new (); if (get_completion_flags (cp) & CFLAG_FILENAME) astr_cat (bs, get_completion_path (cp)); astr_ncat_cstr (bs, get_completion_match (cp), get_completion_matchsize (cp)); if (strncmp (astr_cstr (as), astr_cstr (bs), astr_len (bs)) != 0) thistab = -1; astr_delete (as); as = bs; pos = astr_len (as); break; } case COMPLETION_NOTMATCHED: ding (); } } break; case ' ': if (cp != NULL) goto got_tab; /* FALLTHROUGH */ default: if (c > 255 || !isprint (c)) { ding (); break; } astr_insert_char (as, pos++, c); if (overwrite_mode && pos != astr_len (as)) astr_remove (as, pos, 1); } lasttab = thistab; } }
/* * This functions does some corrections and expansions to * the passed path: * - expands `~/' and `~name/' expressions; * - replaces `//' with `/' (restarting from the root directory); * - removes `..' and `.' entries. * * If something goes wrong, the string is deleted and NULL returned */ astr expand_path(astr path) { int ret = TRUE; struct passwd *pw; const char *sp = astr_cstr(path); astr epath = astr_new(); if (*sp != '/') { astr_cat_delete(epath, agetcwd()); if (astr_len(epath) == 0 || *astr_char(epath, -1) != '/') astr_cat_char(epath, '/'); } while (*sp != '\0') { if (*sp == '/') { if (*++sp == '/') { /* Got `//'. Restart from this point. */ while (*sp == '/') sp++; astr_truncate(epath, 0); } astr_cat_char(epath, '/'); } else if (*sp == '~') { if (*(sp + 1) == '/') { /* Got `~/'. Restart from this point and insert the user's home directory. */ astr_truncate(epath, 0); if ((pw = getpwuid(getuid())) == NULL) { ret = FALSE; break; } if (strcmp(pw->pw_dir, "/") != 0) astr_cat_cstr(epath, pw->pw_dir); ++sp; } else { /* Got `~something'. Restart from this point and insert that user's home directory. */ astr as = astr_new(); astr_truncate(epath, 0); ++sp; while (*sp != '\0' && *sp != '/') astr_cat_char(as, *sp++); pw = getpwnam(astr_cstr(as)); astr_delete(as); if (pw == NULL) { ret = FALSE; break; } astr_cat_cstr(epath, pw->pw_dir); } } else if (*sp == '.') { if (*(sp + 1) == '/' || *(sp + 1) == '\0') { ++sp; if (*sp == '/' && *(sp + 1) != '/') ++sp; } else if (*(sp + 1) == '.' && (*(sp + 2) == '/' || *(sp + 2) == '\0')) { if (astr_len(epath) >= 1 && *astr_char(epath, -1) == '/') astr_truncate(epath, -1); while (*astr_char(epath, -1) != '/' && astr_len(epath) >= 1) astr_truncate(epath, -1); sp += 2; if (*sp == '/' && *(sp + 1) != '/') ++sp; } else goto got_component; } else { const char *p; got_component: p = sp; while (*p != '\0' && *p != '/') p++; if (*p == '\0') { /* Final filename */ astr_cat_cstr(epath, sp); break; } else { /* Non-final directory */ while (*sp != '/') astr_cat_char(epath, *sp++); } } } astr_cpy(path, epath); astr_delete(epath); if (!ret) { astr_delete(path); return NULL; } return path; }