/* * Convert a key code sequence into a key code sequence string. */ astr keyvectostr(size_t *keys, size_t numkeys) { size_t i; astr as = astr_new(); for (i = 0; i < numkeys; i++) { astr key = chordtostr(keys[i]); astr_cat(as, key); astr_delete(key); if (i < numkeys - 1) astr_cat_char(as, ' '); } return as; }
/* * Convert a key like "\\C-xrs" to "C-x r s" */ astr simplify_key(char *key) { int i, j; size_t *keys; astr dest = astr_new(); if (key == NULL) return dest; i = keystrtovec(key, &keys); for (j = 0; j < i; j++) { astr as; if (j > 0) astr_cat_char(dest, ' '); as = chordtostr(keys[j]); astr_cat(dest, as); astr_delete(as); } if (i > 0) free(keys); return dest; }
/* * 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; } }