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; } } } }
/* interface completion */ static void command_completion(split_arg_t *arg) { struct command_completion_args args = { .arg = arg, .typed = arg->ntcopy, .func = command_name }; tab_completion(&args); } /* method completion */ static void method_completion(const struct interface *iface, split_arg_t *arg) { struct command_completion_args args = { .arg = arg, .typed = arg->next->ntcopy, .func = methods_name, .user = (void *) iface }; if (iface == NULL) return; tab_completion(&args); } static const char *bold = "\x1b[1m"; static const char *normal = "\x1b[0m"; static bool find_nth_argument(const char *str, int n, const char **s, const char **e) { const char *p = str; int argc = 0; *e = NULL; while (p != NULL && *p != 0) { while (isspace(*p)) ++p; if (n == argc) *s = p; if (*p == '[') { p = strchr(p, ']'); if (p != NULL) *e = ++p; } else if (*p == '<') { p = strchr(p, '>'); if (p != NULL) *e = ++p; } else { *e = strchr(p, ' '); if (*e == NULL) *e = p + strlen(p); p = *e; } if (n == argc) break; argc++; *e = NULL; } return *e != NULL; } /* prints short help on method for interface */ static void method_help(struct command_completion_args *args) { int argc; const split_arg_t *arg = args->arg; const char *sb = NULL; const char *eb = NULL; const char *arg1 = ""; int arg1_size = 0; /* size of method field (for methods > 0) */ if (args->user_help == NULL) return; for (argc = 0; arg != NULL; argc++) arg = arg->next; /* Check if this is method from interface */ if (get_command(args->arg->ntcopy) == NULL) { /* if so help is missing interface and method name */ arg1 = args->arg->next->ntcopy; arg1_size = strlen(arg1) + 1; } find_nth_argument(args->user_help, argc - (arg1_size ? 3 : 2), &sb, &eb); if (eb != NULL) haltest_info("%s %-*s%.*s%s%.*s%s%s\n", args->arg->ntcopy, arg1_size, arg1, (int) (sb - args->user_help), args->user_help, bold, (int) (eb - sb), sb, normal, eb); else haltest_info("%s %-*s%s\n", args->arg->ntcopy, arg1_size, arg1, args->user_help); } /* So we have empty enumeration */ static const char *return_null(void *user, int i) { return NULL; } /* * parameter completion function * argc - number of elements in arg list * arg - list of arguments * method - method to get completion from (can be NULL) */ static void param_completion(int argc, const split_arg_t *arg, const struct method *method, int hlpix) { int i; const char *argv[argc]; const split_arg_t *tmp = arg; struct command_completion_args args = { .arg = arg, .func = return_null }; /* prepare standard argv from arg */ for (i = 0; i < argc; ++i) { argv[i] = tmp->ntcopy; tmp = tmp->next; } if (method != NULL && method->complete != NULL) { /* ask method for completion function */ method->complete(argc, argv, &args.func, &args.user); } /* If method provided enumeration function call try to complete */ if (args.func != NULL) { args.typed = argv[argc - 1]; args.help = method_help; args.user_help = method ? method->help : NULL; tab_completion(&args); } }