cparser_result_t cparser_line_prev_line(cparser_t *parser) { int n; if (!VALID_PARSER(parser)) { return CPARSER_ERR_INVALID_PARAMS; } /* * Erase the current line */ for (n = 0; n < cparser_line_last(parser); n++) { parser->cfg.prints(parser, "\b \b"); } /* * Go to the previous line */ parser->cur_line--; if (0 > parser->cur_line) { parser->cur_line = parser->max_line; } /* * Print out the new line */ cparser_line_print(parser, 0, 0); return CPARSER_OK; }
cparser_result_t cparser_line_delete (cparser_t *parser) { cparser_line_t *line; int n; if (!VALID_PARSER(parser)) { return CPARSER_ERR_INVALID_PARAMS; } line = &parser->lines[parser->cur_line]; assert(line->current <= line->last); if (!line->last || !line->current) { /* Line is empty or we're at the beginning of the line */ return CPARSER_ERR_NOT_EXIST; } /* Move all character after current position back by one */ for (n = line->current; n < line->last; n++) { line->buf[n-1] = line->buf[n]; } line->current--; line->last--; line->buf[line->last] = '\0'; /* Update the display */ parser->cfg.printc(parser, '\b'); parser->cfg.prints(parser, LINE_CURRENT(line)); parser->cfg.prints(parser, " \b"); for (n = line->current; n < line->last; n++) { parser->cfg.printc(parser, '\b'); } return CPARSER_OK; }
char cparser_line_char(const cparser_t *parser, short pos) { const cparser_line_t *line; assert(VALID_PARSER(parser)); line = &parser->lines[parser->cur_line]; assert(pos < line->last); return line->buf[pos]; }
char cparser_line_current_char (const cparser_t *parser) { const cparser_line_t *line; assert(VALID_PARSER(parser)); line = &parser->lines[parser->cur_line]; return line->buf[line->current]; }
cparser_result_t cparser_walk(cparser_t *parser, cparser_walker_fn pre_fn, cparser_walker_fn post_fn, void *cookie) { if (!VALID_PARSER(parser) || (!pre_fn && !post_fn)) { return CPARSER_ERR_INVALID_PARAMS; } return cparser_walk_internal(parser, parser->root[parser->root_level], pre_fn, post_fn, cookie); }
char cparser_line_prev_char(cparser_t *parser) { cparser_line_t *line; assert(VALID_PARSER(parser)); line = &parser->lines[parser->cur_line]; if (!line->current) { /* * Already at the beginning of the line */ return 0; } parser->cfg.printc(parser, '\b'); line->current--; return '\b'; }
/** * \brief Generate context-sensitive help. * * \param parser Pointer to the parser structure. */ static cparser_result_t cparser_help(cparser_t *parser) { cparser_node_t *node; cparser_token_t *token; int local_is_complete; assert(VALID_PARSER(parser)); if (CPARSER_STATE_WHITESPACE == parser->state) { /* * Just print out every children */ for (node = parser->cur_node->children; NULL != node; node = node->sibling) { cparser_help_print_node(parser, node, 1, 1); } } else if (CPARSER_STATE_ERROR == parser->state) { /* * We have some problem parsing. Just print out the last known * good parse point and list the valid options. */ cparser_print_error(parser, "Last known good parse point."); for (node = parser->cur_node->children; NULL != node; node = node->sibling) { cparser_help_print_node(parser, node, 1, 1); } } else { /* * We have a partial match */ node = parser->cur_node->children; token = CUR_TOKEN(parser); for (node = parser->cur_node->children; NULL != node; node = node->sibling) { if (!NODE_USABLE(parser, node)) { continue; } if (CPARSER_OK == cparser_match_fn_tbl[node->type](token->buf, token->token_len, node, &local_is_complete)) { cparser_help_print_node(parser, node, 1, 1); } } } cparser_line_print(parser, 1, 1); return CPARSER_OK; }
char cparser_line_next_char(cparser_t *parser) { char retval; cparser_line_t *line; assert(VALID_PARSER(parser)); line = &parser->lines[parser->cur_line]; if (line->last == line->current) { /* * Already at the end of the line */ return 0; } retval = line->buf[line->current]; parser->cfg.printc(parser, retval); line->current++; return retval; }
cparser_result_t cparser_line_advance(cparser_t *parser) { int rc; if (!VALID_PARSER(parser)) { return CPARSER_ERR_INVALID_PARAMS; } parser->cur_line++; if (CPARSER_MAX_LINES <= parser->cur_line) { parser->cur_line = 0; } if (parser->max_line < parser->cur_line) { parser->max_line = parser->cur_line; } rc = cparser_line_reset(&parser->lines[parser->cur_line]); assert(0 == rc); return CPARSER_OK; }
cparser_result_t cparser_run(cparser_t *parser) { int ch; cparser_char_t ch_type = 0; if (!VALID_PARSER(parser)) return CPARSER_ERR_INVALID_PARAMS; parser->cfg.io_init(parser); cparser_print_prompt(parser); parser->done = 0; while (!parser->done) { parser->cfg.getch(parser, &ch, &ch_type); cparser_input(parser, ch, ch_type); } /* while not done */ parser->cfg.io_cleanup(parser); return CPARSER_OK; }
void cparser_line_print (const cparser_t *parser, int print_prompt, int new_line) { const cparser_line_t *line; int n; assert(VALID_PARSER(parser)); if (new_line) { parser->cfg.printc(parser, '\n'); } if (print_prompt) { cparser_print_prompt(parser); } line = &parser->lines[parser->cur_line]; parser->cfg.prints(parser, line->buf); /* Move the cursor back the current position */ for (n = line->current; n < line->last; n++) { parser->cfg.printc(parser, '\b'); } }
cparser_result_t cparser_line_insert(cparser_t *parser, char ch) { int n; cparser_line_t *line; if (!VALID_PARSER(parser) || !ch) { return CPARSER_ERR_INVALID_PARAMS; } line = &parser->lines[parser->cur_line]; if (CPARSER_MAX_LINE_SIZE <= line->last) { return CPARSER_ERR_OUT_OF_RES; } /* * Move all characters from current to last back by 1 */ for (n = line->last; n > line->current; n--) { line->buf[n] = line->buf[n - 1]; } line->buf[++line->last] = '\0'; /* * Insert the new character and update the line display. We do not * have full curse support here. Instead, we simply assume all * characters are on the same line and use backspace to move the * cursor. */ line->buf[line->current] = ch; parser->cfg.printc(parser, ch); line->current++; /* update current position */ parser->cfg.prints(parser, LINE_CURRENT(line)); /* * Move cursor back to the current position */ for (n = line->current; n < line->last; n++) { parser->cfg.printc(parser, '\b'); } return CPARSER_OK; }
short cparser_line_last(const cparser_t *parser) { assert(VALID_PARSER(parser)); return parser->lines[parser->cur_line].last; }
cparser_result_t cparser_load_cmd(cparser_t *parser, char *filename) { FILE *fp; char buf[128]; size_t rsize, n; int fd, indent = 0, last_indent = -1, new_line = 1, m, line_num = 0; if (!VALID_PARSER(parser) || !filename) { return CPARSER_ERR_INVALID_PARAMS; } fd = parser->cfg.fd; parser->cfg.fd = -1; fp = fopen(filename, "r"); if (!fp) { return CPARSER_NOT_OK; } cparser_fsm_reset(parser); while (!feof(fp)) { rsize = fread(buf, 1, sizeof(buf), fp); for (n = 0; n < rsize; n++) { /* * Examine the input characters to maintain indent level */ if ('\n' == buf[n]) { cparser_result_t rc; char buf[128]; line_num++; indent = 0; new_line = 1; rc = cparser_execute_cmd(parser); if (CPARSER_OK == rc) { continue; } parser->cfg.fd = fd; switch (rc) { case CPARSER_ERR_PARSE_ERR: snprintf(buf, sizeof(buf), "Line %d: Parse error.\n", line_num); parser->cfg.prints(parser, buf); break; case CPARSER_ERR_INCOMP_CMD: snprintf(buf, sizeof(buf), "Line %d: Incomplete command.\n", line_num); parser->cfg.prints(parser, buf); break; default: assert(0); } return CPARSER_NOT_OK; } else if (' ' == buf[n]) { if (new_line) { indent++; } } else { if (new_line) { new_line = 0; if (indent < last_indent) { for (m = indent; m < last_indent; m++) { if (CPARSER_OK != cparser_submode_exit(parser)) { break; } cparser_fsm_reset(parser); } } last_indent = indent; } } (void)cparser_fsm_input(parser, buf[n]); } } fclose(fp); while (parser->root_level) { (void)cparser_submode_exit(parser); cparser_fsm_reset(parser); } parser->cfg.fd = fd; return CPARSER_OK; }
cparser_result_t cparser_input(cparser_t *parser, char ch, cparser_char_t ch_type) { int n, do_echo; cparser_result_t rc; if (!VALID_PARSER(parser)) { return CPARSER_ERR_INVALID_PARAMS; } if (cparser_is_user_input(parser, &do_echo)) { /* * Process user input */ if (CPARSER_CHAR_REGULAR != ch_type) { return CPARSER_OK; } if ('\n' == ch) { /* * We have a complete input. Call the callback. */ assert(parser->user_input_cb); parser->user_buf[parser->user_buf_count] = '\0'; rc = parser->user_input_cb(parser, parser->user_buf, parser->user_buf_count); cparser_input_reset(parser); cparser_print_prompt(parser); return rc; } if ((parser->cfg.ch_erase == ch) || (parser->cfg.ch_del == ch)) { if (parser->user_buf_count > 0) { parser->user_buf_count--; } if (parser->user_do_echo) { parser->cfg.printc(parser, '\b'); } } else if ((parser->user_buf_count + 1) < parser->user_buf_size) { parser->user_buf[parser->user_buf_count] = ch; parser->user_buf_count++; if (parser->user_do_echo) { parser->cfg.printc(parser, ch); } } return CPARSER_OK; } switch (ch_type) { case CPARSER_CHAR_REGULAR: { if ((parser->cfg.ch_complete == ch) || (parser->cfg.ch_help == ch)) { /* * Completion and help character do not go into the line * buffer. So, do nothing. */ break; } if ((parser->cfg.ch_erase == ch) || (parser->cfg.ch_del == ch)) { rc = cparser_line_delete(parser); assert(CPARSER_ERR_INVALID_PARAMS != rc); if (CPARSER_ERR_NOT_EXIST == rc) { return CPARSER_OK; } } else if ('\n' == ch) { /* * Put the rest of the line into parser FSM */ for (n = cparser_line_current(parser); n < cparser_line_last(parser); n++) { rc = cparser_fsm_input(parser, cparser_line_char(parser, n)); assert(CPARSER_OK == rc); } } else { (void)cparser_line_insert(parser, ch); } break; } case CPARSER_CHAR_UP_ARROW: { rc = cparser_line_prev_line(parser); assert(CPARSER_OK == rc); /* * Reset the token stack and re-enter the command */ cparser_fsm_reset(parser); for (n = 0; n < cparser_line_current(parser); n++) { rc = cparser_fsm_input(parser, cparser_line_char(parser, n)); assert(CPARSER_OK == rc); } return CPARSER_OK; } case CPARSER_CHAR_DOWN_ARROW: { rc = cparser_line_next_line(parser); assert(CPARSER_OK == rc); /* * Reset the token stack and re-enter the command */ cparser_fsm_reset(parser); for (n = 0; n < cparser_line_current(parser); n++) { rc = cparser_fsm_input(parser, cparser_line_char(parser, n)); assert(CPARSER_OK == rc); } return CPARSER_OK; } case CPARSER_CHAR_LEFT_ARROW: { ch = cparser_line_prev_char(parser); if (!ch) { parser->cfg.printc(parser, '\a'); return CPARSER_OK; } break; } case CPARSER_CHAR_RIGHT_ARROW: { ch = cparser_line_next_char(parser); if (!ch) { parser->cfg.printc(parser, '\a'); return CPARSER_OK; } break; } case CPARSER_CHAR_FIRST: { do { ch = cparser_line_prev_char(parser); if (ch) { cparser_fsm_input(parser, ch); } } while (ch); return CPARSER_OK; } case CPARSER_CHAR_LAST: { do { ch = cparser_line_next_char(parser); if (ch) { cparser_fsm_input(parser, ch); } } while (ch); return CPARSER_OK; } default: { /* * An unknown character. Alert and continue */ parser->cfg.printc(parser, '\a'); return CPARSER_NOT_OK; } } /* switch (ch_type) */ /* * Handle special characters */ if (ch == parser->cfg.ch_complete) { while (cparser_complete_one_level(parser)) ; return CPARSER_OK; } else if (ch == parser->cfg.ch_help) { /* * Ask for context sensitve help */ cparser_help(parser); return CPARSER_OK; } else if ('\n' == ch) { rc = cparser_execute_cmd(parser); cparser_line_advance(parser); return rc; } return cparser_fsm_input(parser, (char)ch); }
/** * \brief If the command is not complete, attempt to complete the *command. * If there is a complete comamnd, execute the glue (& action) * function of a command. * * \param parser Pointer to the parser structure. * * \return CPARSER_OK if a valid command is executed; CPARSER_NOT_OK * otherwise. */ static cparser_result_t cparser_execute_cmd(cparser_t *parser) { int do_echo; cparser_result_t rc = CPARSER_OK; assert(VALID_PARSER(parser)); /* * Enter a command. There are three possibilites: * 1. If we are in WHITESPACE state, we check if there is * only one child of keyword type. If yes, we recurse * into it and repeat until either: a) there is more than * one choice, b) We are at a END node. If there is more * than one choice, we look for an END node. In either * case, if an END node is found, we execute the action * function. * * 2. If we are in TOKEN state, we check if we have an unique * match. If yes, re recurse into it and repeat just * like WHITESPACE state until we find an END node. * * 3. If we are in ERROR state, we print out an error. * * Afterward, we reset the parser state and move to the * next line buffer. */ if ((CPARSER_STATE_TOKEN == parser->state) || (CPARSER_STATE_WHITESPACE == parser->state)) { cparser_node_t *child; if (CPARSER_STATE_TOKEN == parser->state) { cparser_token_t *token; cparser_node_t *match; int is_complete; token = CUR_TOKEN(parser); if ((1 <= cparser_match(parser, token->buf, token->token_len, parser->cur_node, &match, &is_complete)) && (is_complete)) { cparser_complete_fn fn = cparser_complete_fn_tbl[match->type]; if (fn) { fn(parser, match, token->buf, token->token_len); } rc = cparser_input(parser, ' ', CPARSER_CHAR_REGULAR); assert(CPARSER_OK == rc); } else { cparser_print_error(parser, "Incomplete command\n"); rc = CPARSER_ERR_INCOMP_CMD; /* * Reset the internal buffer, state and cur_node */ cparser_record_command(parser, rc); cparser_fsm_reset(parser); cparser_print_prompt(parser); return rc; } } /* * Look for a single keyword node child */ child = parser->cur_node->children; assert(child); while ((CPARSER_NODE_KEYWORD == child->type) && NODE_USABLE(parser, child) && (!child->sibling)) { cparser_token_t *token = CUR_TOKEN(parser); cparser_complete_keyword(parser, child, token->buf, token->token_len); rc = cparser_input(parser, ' ', CPARSER_CHAR_REGULAR); assert(CPARSER_OK == rc); child = parser->cur_node->children; assert(child); } /* * Look for an end node */ child = parser->cur_node->children; while ((NULL != child) && !((CPARSER_NODE_END == child->type) && NODE_USABLE(parser, child))) { child = child->sibling; } if (child) { assert(CPARSER_NODE_END == child->type); /* * Execute the glue function */ parser->cur_node = child; parser->cfg.printc(parser, '\n'); rc = ((cparser_glue_fn)child->param)(parser); } else { if (parser->token_tos) { cparser_print_error(parser, "Incomplete command\n"); rc = CPARSER_ERR_INCOMP_CMD; } /* * Reset FSM states and advance to the next line */ cparser_record_command(parser, rc); cparser_fsm_reset(parser); if (CPARSER_OK == rc) { /* * This is just a blank line */ parser->cfg.printc(parser, '\n'); } cparser_print_prompt(parser); return rc; } } else if (CPARSER_STATE_ERROR == parser->state) { cparser_print_error(parser, "Parse error\n"); rc = CPARSER_ERR_PARSE_ERR; } /* * Reset FSM states and advance to the next line */ cparser_record_command(parser, rc); cparser_fsm_reset(parser); if (!cparser_is_user_input(parser, &do_echo)) { cparser_print_prompt(parser); } return rc; }