/* * Return a `~/foo' like path if the user is under his home directory, * and restart from / if // found, * else the unmodified path. */ astr compact_path(const astr path) { astr buf = astr_new(); struct passwd *pw; size_t i; if ((pw = getpwuid(getuid())) == NULL) { /* User not found in password file. */ astr_cpy(buf, path); return buf; } /* Replace `/userhome/' (if existent) with `~/'. */ i = strlen(pw->pw_dir); if (!strncmp(pw->pw_dir, astr_cstr(path), i)) { astr_cpy_cstr(buf, "~/"); if (!strcmp(pw->pw_dir, "/")) astr_cat_cstr(buf, astr_char(path, 1)); else astr_cat_cstr(buf, astr_char(path, i + 1)); } else astr_cpy(buf, path); return buf; }
static astr astr_replace_x(astr as, int pos, size_t size, const char *s, size_t csize) { astr dest = astr_new(); if (pos < 0) { pos = as->size + pos; if (pos < 0) pos = 0; } if ((unsigned int)pos > as->size) pos = as->size; if (as->size - pos < size) size = as->size - pos; if (size > 0) { dest->size = as->size - size + csize; resize_if_smaller(dest, dest->size); memcpy(dest->text, as->text, pos); memcpy(dest->text + pos, s, csize); strcpy(dest->text + pos + csize, as->text + pos + size); } free(as->text); *as = *dest; free(dest); return as; }
astr astr_copy(castr as) { astr dest; assert(as != NULL); dest = astr_new(); astr_assign(dest, as); return dest; }
astr astr_copy_cstr(const char *s) { astr dest; assert(s != NULL); dest = astr_new(); astr_assign_cstr(dest, s); return dest; }
astr astr_right(castr as, size_t size) { assert(as != NULL); if (size == 0) return astr_new(); if (size > as->size) size = as->size; return astr_substr(as, as->size - size, size); }
astr astr_left(castr as, size_t size) { assert(as != NULL); if (size == 0) return astr_new(); if (size > as->size) size = as->size; return astr_substr(as, 0, 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); }
astr variableDump(le *varlist) { astr as = astr_new(); for (; varlist; varlist = varlist->list_next) { if (varlist->branch) { astr_afmt(as, "%s \t", varlist->data); astr_cat_delete(as, leDumpReformat(varlist->branch)); astr_cat_char(as, '\n'); } } return as; }
/* * 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; }
/* * Return a string of maximum length `maxlen' beginning with a `...' * sequence if a cut is need. */ astr shorten_string (char *s, int maxlen) { astr as = astr_new (); int len = strlen (s); if (len <= maxlen) astr_cpy_cstr (as, s); else { astr_cpy_cstr (as, "..."); astr_cat_cstr (as, s + len - maxlen + 3); } return as; }
/* * Allocate a new completion structure. */ Completion * completion_new (int fileflag) { Completion *cp = (Completion *) XZALLOC (Completion); cp->completions = gl_list_create_empty (GL_LINKED_LIST, completion_streq, NULL, (gl_listelement_dispose_fn) free, false); cp->matches = gl_list_create_empty (GL_LINKED_LIST, completion_streq, NULL, NULL, false); if (fileflag) { cp->path = astr_new (); cp->flags |= CFLAG_FILENAME; } return cp; }
bool find_file (const char *filename) { Buffer bp; for (bp = head_bp; bp != NULL; bp = get_buffer_next (bp)) if (get_buffer_filename (bp) != NULL && STREQ (get_buffer_filename (bp), filename)) break; if (bp == NULL) { if (exist_file (filename) && !is_regular_file (filename)) { minibuf_error ("File exists but could not be read"); return false; } else { bp = buffer_new (); set_buffer_names (bp, filename); set_buffer_dir (bp, astr_new_cstr (dir_name (filename))); estr es = estr_readf (filename); if (es) set_buffer_readonly (bp, !check_writable (filename)); else es = estr_new_astr (astr_new ()); set_buffer_text (bp, es); /* Reset undo history. */ set_buffer_next_undop (bp, NULL); set_buffer_last_undop (bp, NULL); set_buffer_modified (bp, false); } } switch_to_buffer (bp); thisflag |= FLAG_NEED_RESYNC; return true; }
/* * 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; }
astr astr_substr(castr as, int pos, size_t size) { astr dest; assert(as != NULL); dest = astr_new(); if (pos < 0) { pos = as->size + pos; if (pos < 0) pos = 0; } if ((unsigned int)pos > as->size) pos = as->size; if (as->size - pos < size) size = as->size - pos; if (size > 0) { resize_if_smaller(dest, size); memcpy(dest->text, as->text + pos, size); dest->size = size; } return dest; }
/* * Read the file contents into a buffer. * Return quietly if the file doesn't exist, or other error. */ void read_from_disk(const char *filename) { Line *lp; FILE *fp; int i, size, first_eol = TRUE; char *this_eol_type; size_t eol_len = 0, total_eols = 0; char buf[BUFSIZ]; if ((fp = fopen(filename, "r")) == NULL) { if (errno != ENOENT) { minibuf_write("%s: %s", filename, strerror(errno)); cur_bp->flags |= BFLAG_READONLY; } return; } #if HAVE_UNISTD_H if (!check_writable(filename)) cur_bp->flags |= BFLAG_READONLY; #endif lp = cur_bp->pt.p; /* Read first chunk and determine EOL type. */ if ((size = fread(buf, 1, BUFSIZ, fp)) > 0) { for (i = 0; i < size && total_eols < MAX_EOL_CHECK_COUNT; i++) { if (buf[i] == '\n' || buf[i] == '\r') { total_eols++; if (buf[i] == '\n') this_eol_type = coding_eol_lf; else if (i >= size || buf[i + 1] != '\n') this_eol_type = coding_eol_cr; else { this_eol_type = coding_eol_crlf; i++; } if (first_eol) { /* This is the first end-of-line. */ cur_bp->eol = this_eol_type; first_eol = FALSE; } else if (cur_bp->eol != this_eol_type) { /* This EOL is different from the last; arbitrarily choose LF. */ cur_bp->eol = coding_eol_lf; break; } } } /* Process this and subsequent chunks into lines. */ eol_len = strlen(cur_bp->eol); do { for (i = 0; i < size; i++) { if (strncmp(cur_bp->eol, buf + i, eol_len) != 0) astr_cat_char(lp->item, buf[i]); else { lp = list_prepend(lp, astr_new()); ++cur_bp->num_lines; i += eol_len - 1; } } } while ((size = fread(buf, 1, BUFSIZ, fp)) > 0); } list_next(lp) = cur_bp->lines; list_prev(cur_bp->lines) = lp; cur_bp->pt.p = list_next(cur_bp->lines); fclose(fp); }
/* * 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; }
static astr snagAToken(getcCallback getachar, ungetcCallback ungetachar, enum tokenname *tokenid) { int c; int doublequotes = 0; astr tok = astr_new(); *tokenid = T_EOF; /* Chew space to next token */ do { c = getachar(); /* Munch comments */ if (c == ';') do { c = getachar(); } while (c != EOF && c != '\n'); } while (c != EOF && (c == ' ' || c == '\t')); /* Snag token */ if (c == '(') { *tokenid = T_OPENPAREN; return tok; } else if (c == ')') { *tokenid = T_CLOSEPAREN; return tok; } else if (c == '\'') { *tokenid = T_QUOTE; return tok; } else if (c == '\n') { *tokenid = T_NEWLINE; return tok; } else if (c == EOF) { *tokenid = T_EOF; return tok; } /* It looks like a string. Snag to the next whitespace. */ if (c == '\"') { doublequotes = 1; c = getachar(); } while (1) { astr_cat_char(tok, (char)c); if (!doublequotes) { if (c == ')' || c == '(' || c == ';' || c == ' ' || c == '\n' || c == '\r' || c == EOF) { ungetachar(c); astr_truncate(tok, (ptrdiff_t)-1); if (!astr_cmp_cstr(tok, "quote")) { *tokenid = T_QUOTE; return tok; } *tokenid = T_WORD; return tok; } } else { switch (c) { case '\n': case '\r': case EOF: ungetachar(c); /* Fall through */ case '\"': astr_truncate(tok, (ptrdiff_t)-1); *tokenid = T_WORD; return tok; } } c = getachar(); } return tok; }
/* * Convert a key chord into its ASCII representation */ astr chordtostr(size_t key) { astr as = astr_new(); if (key & KBD_CTRL) astr_cat_cstr(as, "C-"); if (key & KBD_META) astr_cat_cstr(as, "M-"); key &= ~(KBD_CTRL | KBD_META); switch (key) { case KBD_PGUP: astr_cat_cstr(as, "<prior>"); break; case KBD_PGDN: astr_cat_cstr(as, "<next>"); break; case KBD_HOME: astr_cat_cstr(as, "<home>"); break; case KBD_END: astr_cat_cstr(as, "<end>"); break; case KBD_DEL: astr_cat_cstr(as, "<delete>"); break; case KBD_BS: astr_cat_cstr(as, "<backspace>"); break; case KBD_INS: astr_cat_cstr(as, "<insert>"); break; case KBD_LEFT: astr_cat_cstr(as, "<left>"); break; case KBD_RIGHT: astr_cat_cstr(as, "<right>"); break; case KBD_UP: astr_cat_cstr(as, "<up>"); break; case KBD_DOWN: astr_cat_cstr(as, "<down>"); break; case KBD_RET: astr_cat_cstr(as, "<RET>"); break; case KBD_TAB: astr_cat_cstr(as, "<TAB>"); break; case KBD_F1: astr_cat_cstr(as, "<f1>"); break; case KBD_F2: astr_cat_cstr(as, "<f2>"); break; case KBD_F3: astr_cat_cstr(as, "<f3>"); break; case KBD_F4: astr_cat_cstr(as, "<f4>"); break; case KBD_F5: astr_cat_cstr(as, "<f5>"); break; case KBD_F6: astr_cat_cstr(as, "<f6>"); break; case KBD_F7: astr_cat_cstr(as, "<f7>"); break; case KBD_F8: astr_cat_cstr(as, "<f8>"); break; case KBD_F9: astr_cat_cstr(as, "<f9>"); break; case KBD_F10: astr_cat_cstr(as, "<f10>"); break; case KBD_F11: astr_cat_cstr(as, "<f11>"); break; case KBD_F12: astr_cat_cstr(as, "<f12>"); break; case ' ': astr_cat_cstr(as, "SPC"); break; default: if (isgraph(key)) astr_cat_char(as, (int)(key & 0xff)); else astr_afmt(as, "<%x>", key); } return as; }
int main(void) { astr as1, as2, as3; int i; as1 = astr_new(); astr_assign_cstr(as1, " world"); astr_prepend_cstr(as1, "hello"); astr_append_char(as1, '!'); assert_eq(as1, "hello world!"); as3 = astr_substr(as1, 6, 5); assert_eq(as3, "world"); as2 = astr_new(); astr_assign_cstr(as2, " "); astr_prepend_cstr(as2, "The"); astr_append(as2, as3); astr_append_char(as2, '.'); assert_eq(as2, "The world."); astr_delete(as3); as3 = astr_substr(as1, -6, 5); assert_eq(as3, "world"); astr_assign_cstr(as1, "12345"); astr_delete(as2); as2 = astr_left(as1, 10); assert_eq(as2, "12345"); astr_delete(as2); as2 = astr_left(as1, 3); assert_eq(as2, "123"); astr_delete(as2); as2 = astr_right(as1, 10); assert_eq(as2, "12345"); astr_delete(as2); as2 = astr_right(as1, 3); assert_eq(as2, "345"); astr_assign_cstr(as1, "12345"); astr_insert_cstr(as1, 3, "mid"); astr_insert_cstr(as1, 0, "begin"); astr_insert_cstr(as1, 100, "end"); assert_eq(as1, "begin123mid45end"); astr_assign_cstr(as1, "12345"); astr_insert_char(as1, -2, 'x'); astr_insert_char(as1, -10, 'y'); astr_insert_char(as1, 10, 'z'); assert_eq(as1, "y123x45z"); astr_assign_cstr(as1, "12345"); astr_delete(as2); as2 = astr_substr(as1, -2, 5); assert_eq(as2, "45"); astr_assign_cstr(as1, "12345"); astr_delete(as2); as2 = astr_substr(as1, -10, 5); assert_eq(as2, "12345"); astr_assign_cstr(as1, "1234567"); astr_replace_cstr(as1, -4, 2, "foo"); assert_eq(as1, "123foo67"); astr_assign_cstr(as1, "1234567"); astr_replace_cstr(as1, 1, 3, "foo"); assert_eq(as1, "1foo567"); astr_assign_cstr(as1, "1234567"); astr_replace_cstr(as1, -1, 5, "foo"); assert_eq(as1, "123456foo"); astr_assign_cstr(as1, "1234567"); astr_remove(as1, 4, 10); assert_eq(as1, "1234"); astr_assign_cstr(as1, "abc def de ab cd ab de fg"); while ((i = astr_find_cstr(as1, "de")) >= 0) astr_replace_cstr(as1, i, 2, "xxx"); assert_eq(as1, "abc xxxf xxx ab cd ab xxx fg"); while ((i = astr_find_cstr(as1, "ab")) >= 0) astr_remove(as1, i, 2); assert_eq(as1, "c xxxf xxx cd xxx fg"); while ((i = astr_find_cstr(as1, " ")) >= 0) astr_replace_char(as1, i, 2, ' '); assert_eq(as1, "c xxxf xxx cd xxx fg"); astr_fill(as1, 'x', 3); assert_eq(as1, "xxx"); astr_fmt(as1, "%s * %d = ", "5", 3); astr_afmt(as1, "%d", 15); assert_eq(as1, "5 * 3 = 15"); printf("Input one string: "); fflush(stdout); astr_fgets(as1, stdin); printf("You wrote: \"%s\"\n", astr_cstr(as1)); astr_delete(as1); astr_delete(as2); astr_delete(as3); printf("astr test successful.\n"); return 0; }
/* * 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; } }