Fork us on GitHub Follow us on Facebook Follow us on Twitter

Changeset a0fc4be in mainline


Ignore:
Timestamp:
2011-08-20T06:51:43Z (10 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master
Children:
0f4532e, 5fb32c5, abf04a54
Parents:
e3e4a2c (diff), b2727f18 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge Martin Sucha's improvements to bdsh completion:

  • improved tokenizer
  • use tokenizer also in completion
  • display completion options in columns
Location:
uspace
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • uspace/app/bdsh/compl.c

    re3e4a2c ra0fc4be  
    11/*
    22 * Copyright (c) 2011 Jiri Svoboda
     3 * Copyright (c) 2011 Martin Sucha
    34 * All rights reserved.
    45 *
     
    3738#include "compl.h"
    3839#include "exec.h"
     40#include "tok.h"
    3941
    4042static int compl_init(wchar_t *text, size_t pos, size_t *cstart, void **state);
     
    8890{
    8991        compl_t *cs = NULL;
    90         size_t p;
    9192        size_t pref_size;
    9293        char *stext = NULL;
     
    9697        static const char *dirlist_arg[] = { ".", NULL };
    9798        int retval;
     99        tokenizer_t tok;
     100        token_t tokens[WORD_MAX];
     101        unsigned int current_token;
     102        size_t tokens_length;
    98103
    99104        cs = calloc(1, sizeof(compl_t));
     
    103108        }
    104109
    105         /*
    106          * Copy token pointed to by caret from start up to the caret.
    107          * XXX Ideally we would use the standard tokenizer.
    108          */
    109         p = pos;
    110         while (p > 0 && text[p - 1] != (wchar_t) ' ')
    111                 --p;
    112         *cstart = p;
    113 
    114110        /* Convert text buffer to string */
    115         stext = wstr_to_astr(text + *cstart);
     111        stext = wstr_to_astr(text);
    116112        if (stext == NULL) {
    117113                retval = ENOMEM;
    118114                goto error;
    119115        }
    120 
    121         /* Extract the prefix being completed */
     116       
     117        /* Tokenize the input string */
     118        retval = tok_init(&tok, stext, tokens, WORD_MAX);
     119        if (retval != EOK) {
     120                goto error;
     121        }
     122       
     123        retval = tok_tokenize(&tok, &tokens_length);
     124        if (retval != EOK) {
     125                goto error;
     126        }
     127       
     128        /* Find the current token */
     129        for (current_token = 0; current_token < tokens_length; current_token++) {
     130                token_t *t = &tokens[current_token];
     131                size_t end = t->char_start + t->char_length;
     132                /* Check if the caret lies inside the token or immediately
     133                 * after it
     134                 */
     135                if (t->char_start <= pos && pos <= end) {
     136                        break;
     137                }
     138        }
     139       
     140        if (tokens[current_token].type != TOKTYPE_SPACE) {
     141                *cstart = tokens[current_token].char_start;
     142        }
     143        else {
     144                *cstart = pos;
     145        }
     146       
     147        /* Extract the prefix being completed
     148         * XXX: handle strings, etc.
     149         */
    122150        pref_size = str_lsize(stext, pos - *cstart);
    123151        prefix = malloc(pref_size + 1);
     
    127155        }
    128156
    129         str_ncpy(prefix, pref_size + 1, stext, pref_size);
     157        str_ncpy(prefix, pref_size + 1, stext +
     158            tokens[current_token].byte_start, pref_size);
    130159
    131160        /*
     
    133162         * We look at the previous token. If there is none or it is a pipe
    134163         * ('|'), it is a command, otherwise it is an argument.
    135          * XXX Again we should use the standard tokenizer/parser.
    136164         */
    137165
    138166        /* Skip any whitespace before current token */
    139         while (p > 0 && text[p - 1] == (wchar_t) ' ')
    140                 --p;
     167        int prev_token = current_token - 1;
     168        if (prev_token != -1 && tokens[prev_token].type == TOKTYPE_SPACE) {
     169                prev_token--;
     170        }
    141171
    142172        /*
     
    144174         * follows a pipe token.
    145175         */
    146         if (p == 0 || text[p - 1] == '|')
     176        if (prev_token == -1 || tokens[prev_token].type == TOKTYPE_SPACE)
    147177                cs->is_command = true;
    148178        else
     
    189219
    190220        cs->prefix_len = str_length(cs->prefix);
     221       
     222        tok_fini(&tok);
    191223
    192224        *state = cs;
     
    195227error:
    196228        /* Error cleanup */
     229       
     230        tok_fini(&tok);
    197231
    198232        if (cs != NULL && cs->path_list != NULL) {
  • uspace/app/bdsh/input.c

    re3e4a2c ra0fc4be  
    22 * Copyright (c) 2008 Tim Post
    33 * Copyright (c) 2011 Jiri Svoboda
     4 * Copyright (c) 2011 Martin Sucha
    45 * All rights reserved.
    56 *
     
    6768{
    6869        char *cmd[WORD_MAX];
     70        token_t tokens_space[WORD_MAX];
     71        token_t *tokens = tokens_space;
    6972        int rc = 0;
    7073        tokenizer_t tok;
    71         int i, pipe_count, processed_pipes;
    72         int pipe_pos[2];
    73         char **actual_cmd;
     74        unsigned int i, pipe_count, processed_pipes;
     75        unsigned int pipe_pos[2];
    7476        char *redir_from = NULL;
    7577        char *redir_to = NULL;
     
    7880                return CL_EFAIL;
    7981
    80         rc = tok_init(&tok, usr->line, cmd, WORD_MAX);
     82        rc = tok_init(&tok, usr->line, tokens, WORD_MAX);
    8183        if (rc != EOK) {
    8284                goto finit;
    8385        }
    8486       
    85         rc = tok_tokenize(&tok);
     87        size_t tokens_length;
     88        rc = tok_tokenize(&tok, &tokens_length);
    8689        if (rc != EOK) {
    8790                goto finit;
     91        }
     92       
     93        if (tokens_length > 0 && tokens[0].type == TOKTYPE_SPACE) {
     94                tokens++;
     95                tokens_length--;
     96        }
     97       
     98        if (tokens_length > 0 && tokens[tokens_length-1].type == TOKTYPE_SPACE) {
     99                tokens_length--;
    88100        }
    89101       
     
    93105         * First find the pipes and check that there are no more
    94106         */
    95         int cmd_length = 0;
    96         for (i = 0, pipe_count = 0; cmd[i] != NULL; i++, cmd_length++) {
    97                 if (cmd[i][0] == '|') {
     107        for (i = 0, pipe_count = 0; i < tokens_length; i++) {
     108                if (tokens[i].type == TOKTYPE_PIPE) {
    98109                        if (pipe_count >= 2) {
    99110                                print_pipe_usage();
     
    106117        }
    107118       
    108         actual_cmd = cmd;
     119        unsigned int cmd_token_start = 0;
     120        unsigned int cmd_token_end = tokens_length;
     121       
    109122        processed_pipes = 0;
    110123       
    111124        /* Check if the first part (from <file> |) is present */
    112         if (pipe_count > 0 && pipe_pos[0] == 2 && str_cmp(cmd[0], "from") == 0) {
     125        if (pipe_count > 0 && (pipe_pos[0] == 3 || pipe_pos[0] == 4) && str_cmp(tokens[0].text, "from") == 0) {
    113126                /* Ignore the first three tokens (from, file, pipe) and set from */
    114                 redir_from = cmd[1];
    115                 actual_cmd = cmd + 3;
     127                redir_from = tokens[2].text;
     128                cmd_token_start = pipe_pos[0]+1;
    116129                processed_pipes++;
    117130        }
     
    119132        /* Check if the second part (| to <file>) is present */
    120133        if ((pipe_count - processed_pipes) > 0 &&
    121             pipe_pos[processed_pipes] == cmd_length - 3 &&
    122             str_cmp(cmd[cmd_length-2], "to") == 0) {
     134            (pipe_pos[processed_pipes] == tokens_length - 4 ||
     135            (pipe_pos[processed_pipes] == tokens_length - 5 &&
     136            tokens[tokens_length-4].type == TOKTYPE_SPACE )) &&
     137            str_cmp(tokens[tokens_length-3].text, "to") == 0) {
    123138                /* Ignore the last three tokens (pipe, to, file) and set to */
    124                 redir_to = cmd[cmd_length-1];
    125                 cmd[cmd_length-3] = NULL;
    126                 cmd_length -= 3;
     139                redir_to = tokens[tokens_length-1].text;
     140                cmd_token_end = pipe_pos[processed_pipes];
    127141                processed_pipes++;
    128142        }
     
    134148        }
    135149       
    136         if (actual_cmd[0] == NULL) {
     150        /* Convert tokens of the command to string array */
     151        unsigned int cmd_pos = 0;
     152        for (i = cmd_token_start; i < cmd_token_end; i++) {
     153                if (tokens[i].type != TOKTYPE_SPACE) {
     154                        cmd[cmd_pos++] = tokens[i].text;
     155                }
     156        }
     157        cmd[cmd_pos++] = NULL;
     158       
     159        if (cmd[0] == NULL) {
    137160                print_pipe_usage();
    138161                rc = ENOTSUP;
     
    170193        }
    171194       
    172         rc = run_command(actual_cmd, usr, &new_iostate);
     195        rc = run_command(cmd, usr, &new_iostate);
    173196       
    174197finit_with_files:
  • uspace/app/bdsh/tok.c

    re3e4a2c ra0fc4be  
    4242static bool tok_pending_chars(tokenizer_t *);
    4343static int tok_finish_string(tokenizer_t *);
     44static void tok_start_token(tokenizer_t *, token_type_t);
    4445
    4546/** Initialize the token parser
     
    5051 * @param max_tokens number of elements of the out_tokens array
    5152 */
    52 int tok_init(tokenizer_t *tok, char *input, char **out_tokens,
     53int tok_init(tokenizer_t *tok, char *input, token_t *out_tokens,
    5354    size_t max_tokens)
    5455{       
    5556        tok->in = input;
    5657        tok->in_offset = 0;
     58        tok->last_in_offset = 0;
     59        tok->in_char_offset = 0;
     60        tok->last_in_char_offset = 0;
    5761       
    5862        tok->outtok = out_tokens;
    5963        tok->outtok_offset = 0;
    60         /* Leave one slot for a null terminator */
    61         assert(max_tokens > 0);
    62         tok->outtok_size = max_tokens - 1;
     64        tok->outtok_size = max_tokens;
    6365       
    6466        /* Prepare a buffer where all the token strings will be stored */
     
    8789
    8890/** Tokenize the input string into the tokens */
    89 int tok_tokenize(tokenizer_t *tok)
     91int tok_tokenize(tokenizer_t *tok, size_t *tokens_length)
    9092{
    9193        int rc;
    92         wchar_t cur_char;
     94        wchar_t next_char;
    9395       
    9496        /* Read the input line char by char and append tokens */
    95         while ((cur_char = tok_get_char(tok)) != 0) {
    96                 if (cur_char == ' ') {
    97                         /* Spaces delimit tokens, but are not processed in any way
    98                          * Push the token if there is any.
     97        while ((next_char = tok_look_char(tok)) != 0) {
     98                if (next_char == ' ') {
     99                        /* Push the token if there is any.
    99100                         * There may not be any pending char for a token in case
    100101                         * there are several spaces in the input.
     
    106107                                }
    107108                        }
    108                 }
    109                 else if (cur_char == '|') {
    110                         /* Pipes are tokens that are delimiters and should be output
    111                          * as a separate token
     109                        tok_start_token(tok, TOKTYPE_SPACE);
     110                        /* Eat all the spaces */
     111                        while (tok_look_char(tok) == ' ') {
     112                                tok_push_char(tok, tok_get_char(tok));
     113                        }
     114                        tok_push_token(tok);
     115                       
     116                }
     117                else if (next_char == '|') {
     118                        /* Pipes are tokens that are delimiters and should be
     119                         * output as a separate token
    112120                         */
    113121                        if (tok_pending_chars(tok)) {
     
    118126                        }
    119127                       
    120                         rc = tok_push_char(tok, '|');
     128                        tok_start_token(tok, TOKTYPE_PIPE);
     129                       
     130                        rc = tok_push_char(tok, tok_get_char(tok));
    121131                        if (rc != EOK) {
    122132                                return rc;
     
    128138                        }
    129139                }
    130                 else if (cur_char == '\'') {
     140                else if (next_char == '\'') {
    131141                        /* A string starts with a quote (') and ends again with a quote.
    132142                         * A literal quote is written as ''
    133143                         */
     144                        tok_start_token(tok, TOKTYPE_TEXT);
     145                        /* Eat the quote */
     146                        tok_get_char(tok);
    134147                        rc = tok_finish_string(tok);
    135148                        if (rc != EOK) {
     
    138151                }
    139152                else {
     153                        if (!tok_pending_chars(tok)) {
     154                                tok_start_token(tok, TOKTYPE_TEXT);
     155                        }
    140156                        /* If we are handling any other character, just append it to
    141157                         * the current token.
    142158                         */
    143                         rc = tok_push_char(tok, cur_char);
     159                        rc = tok_push_char(tok, tok_get_char(tok));
    144160                        if (rc != EOK) {
    145161                                return rc;
     
    156172        }
    157173       
    158         /* We always have a space for the terminator, as we
    159          * reserved it in tok_init */
    160         tok->outtok[tok->outtok_offset] = 0;
     174        *tokens_length = tok->outtok_offset;
    161175       
    162176        return EOK;
     
    167181{
    168182        int rc;
    169         wchar_t cur_char;
    170        
    171         while ((cur_char = tok_get_char(tok)) != 0) {
    172                 if (cur_char == '\'') {
     183        wchar_t next_char;
     184       
     185        while ((next_char = tok_look_char(tok)) != 0) {
     186                if (next_char == '\'') {
     187                        /* Eat the quote */
     188                        tok_get_char(tok);
    173189                        if (tok_look_char(tok) == '\'') {
    174190                                /* Encode a single literal quote */
     
    187203                }
    188204                else {
    189                         rc = tok_push_char(tok, cur_char);
     205                        rc = tok_push_char(tok, tok_get_char(tok));
    190206                        if (rc != EOK) {
    191207                                return rc;
     
    201217wchar_t tok_get_char(tokenizer_t *tok)
    202218{
     219        tok->in_char_offset++;
    203220        return str_decode(tok->in, &tok->in_offset, STR_NO_LIMIT);
    204221}
     
    207224wchar_t tok_look_char(tokenizer_t *tok)
    208225{
    209         size_t old_offset = tok->in_offset;
     226        unsigned int old_offset = tok->in_offset;
     227        unsigned int old_char_offset = tok->in_char_offset;
    210228        wchar_t ret = tok_get_char(tok);
    211229        tok->in_offset = old_offset;
     230        tok->in_char_offset = old_char_offset;
    212231        return ret;
    213232}
     
    219238}
    220239
     240void tok_start_token(tokenizer_t *tok, token_type_t type)
     241{
     242        tok->current_type = type;
     243}
     244
    221245/** Push the current token to the output array */
    222246int tok_push_token(tokenizer_t *tok)
     
    231255       
    232256        tok->outbuf[tok->outbuf_offset++] = 0;
    233         tok->outtok[tok->outtok_offset++] = tok->outbuf + tok->outbuf_last_start;
     257        token_t *tokinfo = &tok->outtok[tok->outtok_offset++];
     258        tokinfo->type = tok->current_type;
     259        tokinfo->text = tok->outbuf + tok->outbuf_last_start;
     260        tokinfo->byte_start = tok->last_in_offset;
     261        tokinfo->byte_length = tok->in_offset - tok->last_in_offset;
     262        tokinfo->char_start = tok->last_in_char_offset;
     263        tokinfo->char_length = tok->in_char_offset - tok->last_in_char_offset;
    234264        tok->outbuf_last_start = tok->outbuf_offset;
     265       
     266        /* We have consumed the first char of the next token already */
     267        tok->last_in_offset = tok->in_offset;
     268        tok->last_in_char_offset = tok->in_char_offset;
    235269       
    236270        return EOK;
  • uspace/app/bdsh/tok.h

    re3e4a2c ra0fc4be  
    3030#define TOK_H
    3131
     32typedef enum {
     33        TOKTYPE_TEXT,
     34        TOKTYPE_PIPE,
     35        TOKTYPE_SPACE
     36} token_type_t;
     37
     38typedef struct {
     39        char *text;
     40        unsigned int byte_start;
     41        unsigned int char_start;
     42        size_t byte_length;
     43        size_t char_length;
     44        token_type_t type;
     45} token_t;
     46
    3247typedef struct {
    3348        char *in;
    34         size_t in_offset;
     49        unsigned int in_offset;
     50        unsigned int last_in_offset;
     51        unsigned int in_char_offset;
     52        unsigned int last_in_char_offset;
    3553       
    3654        char *outbuf;
     
    3957        size_t outbuf_last_start;
    4058       
    41         char **outtok;
     59        token_t *outtok;
     60        token_type_t current_type;
    4261        size_t outtok_offset;
    4362        size_t outtok_size;
    4463} tokenizer_t;
    4564
    46 extern int tok_init(tokenizer_t *, char *, char **, size_t);
     65extern int tok_init(tokenizer_t *, char *, token_t *, size_t);
    4766extern void tok_fini(tokenizer_t *);
    48 extern int tok_tokenize(tokenizer_t *);
     67extern int tok_tokenize(tokenizer_t *, size_t *);
    4968
    5069#endif
  • uspace/lib/clui/tinput.c

    re3e4a2c ra0fc4be  
    591591}
    592592
     593/* Print a list of completions */
     594static void tinput_show_completions(tinput_t *ti, char **compl, size_t cnum)
     595{
     596        unsigned int i;
     597        /* Determine the maximum length of the completion in chars */
     598        size_t max_length = 0;
     599        for (i = 0; i < cnum; i++)
     600                max_length = max(max_length, str_length(compl[i]));
     601       
     602        unsigned int cols = max(1, (ti->con_cols + 1) / (max_length + 1));
     603        unsigned int col_width = ti->con_cols / cols;
     604        unsigned int rows = cnum / cols + ((cnum % cols) != 0);
     605       
     606        unsigned int row, col;
     607       
     608        for (row = 0; row < rows; row++) {
     609                bool wlc = false;
     610                for (col = 0; col < cols; col++) {
     611                        size_t compl_idx = col * rows + row;
     612                        if (compl_idx >= cnum)
     613                                break;
     614                        if (col)
     615                                printf(" ");
     616                        printf("%s", compl[compl_idx]);
     617                        size_t compl_len = str_length(compl[compl_idx]);
     618                        if (col == cols -1) {
     619                                wlc = (compl_len == max_length);
     620                        }
     621                        else {
     622                                for (i = compl_len; i < col_width; i++) {
     623                                        printf(" ");
     624                                }
     625                        }
     626                }
     627                if (!wlc) printf("\n");
     628        }
     629}
     630
     631
    593632static void tinput_text_complete(tinput_t *ti)
    594633{
     
    676715
    677716                        tinput_jump_after(ti);
    678                         for (i = 0; i < cnum; i++)
    679                                 printf("%s\n", compl[i]);
     717                        tinput_show_completions(ti, compl, cnum);
    680718                        tinput_display(ti);
    681719                }
Note: See TracChangeset for help on using the changeset viewer.