/* * Delete the previous character and collapse all subsequent characters back one */ static void delete_backward() { if (cur_pos > 0) { size_t i; int seqlen = backspace(); max_pos -= seqlen; for (i = cur_pos; i < max_pos; i++) cur_line[i] = cur_line[i + seqlen]; cur_line[max_pos] = '\0'; fix_line(); } }
/* * Delete the character we are on and collapse all subsequent characters back one */ static void delete_forward() { if (cur_pos < max_pos) { size_t i; int seqlen = char_seqlen(); max_pos -= seqlen; for (i = cur_pos; i < max_pos; i++) cur_line[i] = cur_line[i + seqlen]; cur_line[max_pos] = '\0'; fix_line(); } }
/* delete the full or partial word immediately before cursor position */ static void delete_previous_word() { size_t save_pos = cur_pos; /* skip whitespace */ while ((cur_pos > 0) && (cur_line[cur_pos - 1] == SPACE)) { backspace(); } /* find start of previous word */ while ((cur_pos > 0) && (cur_line[cur_pos - 1] != SPACE)) { backspace(); } if (cur_pos != save_pos) { size_t new_cur_pos = cur_pos; size_t m = max_pos - save_pos; /* erase to eol */ while (cur_pos < max_pos) { user_putc(SPACE); if (isdoublewidth(cur_pos)) user_putc(SPACE); cur_pos += char_seqlen(); } while (cur_pos > new_cur_pos) backspace(); /* overwrite previous word with trailing characters */ memmove(cur_line + cur_pos, cur_line + save_pos, m); /* overwrite characters at end of string with NULs */ memset(cur_line + cur_pos + m, NUL, save_pos - cur_pos); /* update display and line length */ max_pos = cur_pos + m; fix_line(); } }
char * readline(const char *prompt) { int cur_char; char *new_line; /* start with a string of MAXBUF chars */ if (line_len != 0) { free(cur_line); line_len = 0; } cur_line = gp_alloc(MAXBUF, "readline"); line_len = MAXBUF; /* set the termio so we can do our own input processing */ set_termio(); /* print the prompt */ fputs(prompt, stderr); cur_line[0] = '\0'; cur_pos = 0; max_pos = 0; cur_entry = NULL; /* get characters */ for (;;) { cur_char = special_getc(); /* Accumulate ascii (7bit) printable characters * and all leading 8bit characters. */ if ((isprint(cur_char) || (((cur_char & 0x80) != 0) && (cur_char != EOF))) && (cur_char != 0x09) /* TAB is a printable character in some locales */ ) { size_t i; if (max_pos + 1 >= line_len) { extend_cur_line(); } for (i = max_pos; i > cur_pos; i--) { cur_line[i] = cur_line[i - 1]; } user_putc(cur_char); cur_line[cur_pos] = cur_char; cur_pos += 1; max_pos += 1; cur_line[max_pos] = '\0'; if (cur_pos < max_pos) { switch (encoding) { case S_ENC_UTF8: if ((cur_char & 0xc0) == 0) fix_line(); /* Normal ascii character */ else if ((cur_char & 0xc0) == 0xc0) ; /* start of a multibyte sequence. */ else if (((cur_char & 0xc0) == 0x80) && ((unsigned char)(cur_line[cur_pos-2]) >= 0xe0)) ; /* second byte of a >2 byte sequence */ else { /* Last char of multi-byte sequence */ fix_line(); } break; default: fix_line(); break; } } /* else interpret unix terminal driver characters */ #ifdef VERASE } else if (cur_char == term_chars[VERASE]) { /* ^H */ delete_backward(); #endif /* VERASE */ #ifdef VEOF } else if (cur_char == term_chars[VEOF]) { /* ^D? */ if (max_pos == 0) { reset_termio(); return ((char *) NULL); } delete_forward(); #endif /* VEOF */ #ifdef VKILL } else if (cur_char == term_chars[VKILL]) { /* ^U? */ clear_line(prompt); #endif /* VKILL */ #ifdef VWERASE } else if (cur_char == term_chars[VWERASE]) { /* ^W? */ delete_previous_word(); #endif /* VWERASE */ #ifdef VREPRINT } else if (cur_char == term_chars[VREPRINT]) { /* ^R? */ putc(NEWLINE, stderr); /* go to a fresh line */ redraw_line(prompt); #endif /* VREPRINT */ #ifdef VSUSP } else if (cur_char == term_chars[VSUSP]) { reset_termio(); kill(0, SIGTSTP); /* process stops here */ set_termio(); /* print the prompt */ redraw_line(prompt); #endif /* VSUSP */ } else { /* do normal editing commands */ /* some of these are also done above */ switch (cur_char) { case EOF: reset_termio(); return ((char *) NULL); case 001: /* ^A */ while (cur_pos > 0) backspace(); break; case 002: /* ^B */ if (cur_pos > 0) backspace(); break; case 005: /* ^E */ while (cur_pos < max_pos) { user_putc(cur_line[cur_pos]); cur_pos += 1; } break; case 006: /* ^F */ if (cur_pos < max_pos) { step_forward(); } break; #if defined(HAVE_DIRENT_H) || defined(WIN32) case 011: /* ^I / TAB */ tab_completion(TRUE); /* next tab completion */ break; case 034: /* remapped by wtext.c or ansi_getc from Shift-Tab */ tab_completion(FALSE); /* previous tab completion */ break; #endif case 013: /* ^K */ clear_eoline(prompt); max_pos = cur_pos; break; case 020: /* ^P */ if (history != NULL) { if (cur_entry == NULL) { cur_entry = history; clear_line(prompt); copy_line(cur_entry->line); } else if (cur_entry->prev != NULL) { cur_entry = cur_entry->prev; clear_line(prompt); copy_line(cur_entry->line); } } break; case 016: /* ^N */ if (cur_entry != NULL) { cur_entry = cur_entry->next; clear_line(prompt); if (cur_entry != NULL) copy_line(cur_entry->line); else cur_pos = max_pos = 0; } break; case 014: /* ^L */ case 022: /* ^R */ putc(NEWLINE, stderr); /* go to a fresh line */ redraw_line(prompt); break; #ifndef DEL_ERASES_CURRENT_CHAR case 0177: /* DEL */ case 023: /* Re-mapped from CSI~3 in ansi_getc() */ #endif case 010: /* ^H */ delete_backward(); break; case 004: /* ^D */ if (max_pos == 0) { reset_termio(); return ((char *) NULL); } /* intentionally omitting break */ #ifdef DEL_ERASES_CURRENT_CHAR case 0177: /* DEL */ case 023: /* Re-mapped from CSI~3 in ansi_getc() */ #endif delete_forward(); break; case 025: /* ^U */ clear_line(prompt); break; case 027: /* ^W */ delete_previous_word(); break; case '\n': /* ^J */ case '\r': /* ^M */ cur_line[max_pos + 1] = '\0'; #ifdef OS2 while (cur_pos < max_pos) { user_putc(cur_line[cur_pos]); cur_pos += 1; } #endif putc(NEWLINE, stderr); /* Shrink the block down to fit the string ? * if the alloc fails, we still own block at cur_line, * but this shouldn't really fail. */ new_line = (char *) gp_realloc(cur_line, strlen(cur_line) + 1, "line resize"); if (new_line) cur_line = new_line; /* else we just hang on to what we had - it's not a problem */ line_len = 0; FPRINTF((stderr, "Resizing input line to %d chars\n", strlen(cur_line))); reset_termio(); return (cur_line); default: break; } } } }
static void tab_completion(TBOOLEAN forward) { size_t i; char * completion; size_t completion_len; static size_t last_tab_pos = -1; static size_t last_completion_len = 0; int direction; /* detect tab cycling */ if ((last_tab_pos + last_completion_len) != cur_pos) { last_completion_len = 0; last_tab_pos = cur_pos; direction = 0; /* new completion */ } else { direction = (forward ? 1 : -1); } /* find completion */ completion = fn_completion(last_tab_pos, direction); if (!completion) return; /* make room for new completion */ completion_len = strlen(completion); if (completion_len > last_completion_len) while (max_pos + completion_len - last_completion_len + 1 > line_len) extend_cur_line(); /* erase from last_tab_pos to eol */ while (cur_pos > last_tab_pos) backspace(); while (cur_pos < max_pos) { user_putc(SPACE); if (isdoublewidth(cur_pos)) user_putc(SPACE); cur_pos += char_seqlen(); } /* rewind to last_tab_pos */ while (cur_pos > last_tab_pos) backspace(); /* insert completion string */ if (max_pos > (last_tab_pos - last_completion_len)) memmove(cur_line + last_tab_pos + completion_len, cur_line + last_tab_pos + last_completion_len, max_pos - last_tab_pos - last_completion_len); memcpy(cur_line + last_tab_pos, completion, completion_len); max_pos += completion_len - last_completion_len; cur_line[max_pos] = NUL; /* draw new completion */ for (i = 0; i < completion_len; i++) user_putc(cur_line[last_tab_pos+i]); cur_pos += completion_len; fix_line(); /* remember this completion */ last_tab_pos = cur_pos - completion_len; last_completion_len = completion_len; }
char * editLine(char *prompt) { /* The line to be edited is stored in cur_line.*/ /* get characters */ int cur_char; for(;;) { cur_char = special_getc(); if(isprint(cur_char) || (((unsigned char)cur_char > 0x7f) && cur_char != EOF) || cur_char == '\t') { int i,inc = 1; if(cur_char == '\t') { inc = TABSTOPS; cur_char = ' '; } if(max_pos+inc>=line_len) extend_cur_line(); for(i=max_pos+inc-1; i-inc>=cur_pos; i--) { cur_line[i] = cur_line[i-inc]; } max_pos += inc; while(inc--) { user_putc(cur_char); cur_line[cur_pos++] = cur_char; } if (cur_pos < max_pos) fix_line(); cur_line[max_pos] = '\0'; #if MATCHPAREN switch(cur_char) { case ')':backupTo('(',')');break; case ']':backupTo('[',']');break; } #endif #if defined(VERASE) } else if(cur_char == term_chars[VERASE] ){ /* DEL? */ if(cur_pos > 0) { int i; cur_pos -= 1; backspace(); for(i=cur_pos; i<max_pos; i++) cur_line[i] = cur_line[i+1]; max_pos -= 1; fix_line(); } } else if(cur_char == term_chars[VEOF] ){ /* ^D? */ if(max_pos == 0) { copy_line("to exit EiC, enter :exit\n"); user_putc(BELL); reset_termio(); return((char*)NULL); } if((cur_pos < max_pos)&&(cur_char == 004)) { /* ^D */ int i; for(i=cur_pos; i<max_pos; i++) cur_line[i] = cur_line[i+1]; max_pos -= 1; fix_line(); } } else if(cur_char == term_chars[VKILL] ){ /* ^U? */ clear_line(prompt); } else if(cur_char == term_chars[VWERASE] ){ /* ^W? */ while((cur_pos > 0) && (cur_line[cur_pos-1] == SPACE)) { cur_pos -= 1; backspace(); } while((cur_pos > 0) && (cur_line[cur_pos-1] != SPACE)) { cur_pos -= 1; backspace(); } clear_eoline(); max_pos = cur_pos; } else if(cur_char == term_chars[VREPRINT] ){ /* ^R? */ user_putc(NEWLINE); /* go to a fresh line */ redraw_line(prompt); } else if(cur_char == term_chars[VSUSP]) { reset_termio(); kill(0, SIGTSTP); /* process stops here */ set_termio(); /* print the prompt */ redraw_line(prompt); #endif } else { /* do normal editing commands */ /* some of these are also done above */ int i; switch(cur_char) { case EOF: reset_termio(); return((char *)NULL); case 001: /* ^A */ while(cur_pos > 0) { cur_pos -= 1; backspace(); } break; case 002: /* ^B */ if(cur_pos > 0) { cur_pos -= 1; backspace(); } break; case 005: /* ^E */ while(cur_pos < max_pos) { user_putc(cur_line[cur_pos]); cur_pos += 1; } break; case 006: /* ^F */ if(cur_pos < max_pos) { user_putc(cur_line[cur_pos]); cur_pos += 1; } break; case 013: /* ^K */ clear_eoline(); max_pos = cur_pos; break; case 020: /* ^P */ if(history != NULL) { if(cur_entry == NULL) { cur_entry = history; clear_line(prompt); copy_line(cur_entry->line); } else if(cur_entry->prev != NULL) { cur_entry = cur_entry->prev; clear_line(prompt); copy_line(cur_entry->line); }else user_putc(BELL); }else user_putc(BELL); break; case 016: /* ^N */ if(cur_entry != NULL) { cur_entry = cur_entry->next; clear_line(prompt); if(cur_entry != NULL) copy_line(cur_entry->line); else cur_pos = max_pos = 0; }else user_putc(BELL); break; case 014: /* ^L */ case 022: /* ^R */ user_putc(NEWLINE); /* go to a fresh line */ redraw_line(prompt); break; case 0177: /* DEL */ case 010: /* ^H */ if(cur_pos > 0) { cur_pos -= 1; backspace(); for(i=cur_pos; i<max_pos; i++) cur_line[i] = cur_line[i+1]; max_pos -= 1; fix_line(); } break; case 004: /* ^D */ if(max_pos == 0) { reset_termio(); return((char *)NULL); } if(cur_pos < max_pos) { for(i=cur_pos; i<max_pos; i++) cur_line[i] = cur_line[i+1]; max_pos -= 1; fix_line(); } break; case 025: /* ^U */ clear_line(prompt); break; case 027: /* ^W */ while((cur_pos > 0) && (cur_line[cur_pos-1] == SPACE)) { cur_pos -= 1; backspace(); } while((cur_pos > 0) && (cur_line[cur_pos-1] != SPACE)) { cur_pos -= 1; backspace(); } clear_eoline(); max_pos = cur_pos; break; case '\n': /* ^J */ case '\r': /* ^M */ user_putc(NEWLINE); cur_line[max_pos+1] = '\0'; cur_line = (char *)ralloc(cur_line, (unsigned long)(strlen(cur_line)+2), "line resize"); line_len=0; reset_termio(); return cur_line; default: break; } } } }