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

Changeset 9be9c4d in mainline


Ignore:
Timestamp:
2011-07-13T19:54:05Z (10 years ago)
Author:
Jiri Svoboda <jiri@…>
Branches:
lfn, master
Children:
3e5c48c9
Parents:
026793d
Message:

Shell command and file-name completion.

Location:
uspace
Files:
2 added
12 edited

Legend:

Unmodified
Added
Removed
  • uspace/app/bdsh/Makefile

    r026793d r9be9c4d  
    5454        cmds/mod_cmds.c \
    5555        cmds/builtin_cmds.c \
     56        compl.c \
    5657        errors.c \
    5758        input.c \
  • uspace/app/bdsh/config.h

    r026793d r9be9c4d  
    4040#endif
    4141
    42 /* Work around for getenv() */
    43 #define PATH "/srv:/app"
    44 #define PATH_DELIM ":"
    45 
    4642/* Used in many places */
    4743#define SMALL_BUFLEN 256
  • uspace/app/bdsh/exec.c

    r026793d r9be9c4d  
    5252static int try_access(const char *);
    5353
     54const char *search_dir[] = { "app", "srv", NULL };
     55
    5456/* work-around for access() */
    5557static int try_access(const char *f)
     
    6971static char *find_command(char *cmd)
    7072{
    71         char *path_tok;
    72         char *path[PATH_MAX];
    73         int n = 0, i = 0;
    74         size_t x = str_size(cmd) + 2;
     73        size_t i;
    7574
    7675        found = (char *)malloc(PATH_MAX);
     
    8180        }
    8281
    83         path_tok = str_dup(PATH);
    84 
    85         /* Extract the PATH env to a path[] array */
    86         path[n] = strtok(path_tok, PATH_DELIM);
    87         while (NULL != path[n]) {
    88                 if ((str_size(path[n]) + x ) > PATH_MAX) {
    89                         cli_error(CL_ENOTSUP,
    90                                 "Segment %d of path is too large, search ends at segment %d",
    91                                 n, n-1);
    92                         break;
    93                 }
    94                 path[++n] = strtok(NULL, PATH_DELIM);
    95         }
    96 
    9782        /* We now have n places to look for the command */
    98         for (i=0; path[i]; i++) {
     83        for (i = 0; search_dir[i] != NULL; i++) {
    9984                memset(found, 0, sizeof(found));
    100                 snprintf(found, PATH_MAX, "%s/%s", path[i], cmd);
     85                snprintf(found, PATH_MAX, "%s/%s", search_dir[i], cmd);
    10186                if (-1 != try_access(found)) {
    102                         free(path_tok);
    10387                        return (char *) found;
    10488                }
     
    10690
    10791        /* We didn't find it, just give it back as-is. */
    108         free(path_tok);
    10992        return (char *) cmd;
    11093}
  • uspace/app/bdsh/exec.h

    r026793d r9be9c4d  
    3333#include "scli.h"
    3434
     35extern const char *search_dir[];
     36
    3537extern unsigned int try_exec(char *, char **, iostate_t *);
    3638
  • uspace/app/bdsh/input.c

    r026793d r9be9c4d  
    11/*
    22 * Copyright (c) 2008 Tim Post
     3 * Copyright (c) 2011 Jiri Svoboda
    34 * All rights reserved.
    45 *
     
    4344
    4445#include "config.h"
     46#include "compl.h"
    4547#include "util.h"
    4648#include "scli.h"
     
    226228        int rc;
    227229       
    228         console_flush(tinput->console);
    229         console_set_style(tinput->console, STYLE_EMPHASIS);
    230         printf("%s", usr->prompt);
    231         console_flush(tinput->console);
    232         console_set_style(tinput->console, STYLE_NORMAL);
     230        tinput_set_prompt(tinput, usr->prompt);
    233231
    234232        rc = tinput_read(tinput, &str);
     
    263261        }
    264262
     263        tinput_set_compl_ops(tinput, &compl_ops);
     264
    265265        return 0;
    266266}
  • uspace/app/bdsh/scli.h

    r026793d r9be9c4d  
    3232#include "config.h"
    3333#include <stdint.h>
     34#include <stdio.h>
    3435
    3536typedef struct {
  • uspace/app/sbi/src/input.c

    r026793d r9be9c4d  
    176176int input_get_line(input_t *input, char **line)
    177177{
     178        const char *prompt;
    178179        const char *sp;
    179180        char *dp;
     
    212213                /* Interactive mode */
    213214                if (input->line_no == 0)
    214                         printf("sbi> ");
     215                        prompt = "sbi> ";
    215216                else
    216                         printf("...  ");
     217                        prompt = "...  ";
    217218
    218219                fflush(stdout);
    219                 if (os_input_line(&line_p) != EOK)
     220                if (os_input_line(prompt, &line_p) != EOK)
    220221                        return EIO;
    221222
  • uspace/app/sbi/src/os/helenos.c

    r026793d r9be9c4d  
    210210 * @param ptr   Place to store pointer to new string.
    211211 */
    212 int os_input_line(char **ptr)
     212int os_input_line(const char *prompt, char **ptr)
    213213{
    214214        char *line;
     
    219219                if (tinput == NULL)
    220220                        return EIO;
     221
     222                tinput_set_prompt(tinput, prompt);
    221223        }
    222224
  • uspace/app/sbi/src/os/os.h

    r026793d r9be9c4d  
    3838char *os_chr_to_astr(wchar_t chr);
    3939void os_input_disp_help(void);
    40 int os_input_line(char **ptr);
     40int os_input_line(const char *prompt, char **ptr);
    4141int os_exec(char * const cmd[]);
    4242
  • uspace/app/sbi/src/os/posix.c

    r026793d r9be9c4d  
    193193 * @param ptr   Place to store pointer to new string.
    194194 */
    195 int os_input_line(char **ptr)
    196 {
     195int os_input_line(const char *prompt, char **ptr)
     196{
     197        printf("%s", prompt);
     198
    197199        if (fgets(os_input_buffer, OS_INPUT_BUFFER_SIZE, stdin) == NULL)
    198200                os_input_buffer[0] = '\0';
  • uspace/lib/clui/tinput.c

    r026793d r9be9c4d  
    11/*
    2  * Copyright (c) 2010 Jiri Svoboda
     2 * Copyright (c) 2011 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    2727 */
    2828
     29#include <sort.h>
    2930#include <stdio.h>
    3031#include <stdlib.h>
     
    4243#include <tinput.h>
    4344
     45#define LIN_TO_COL(ti, lpos) ((lpos) % ((ti)->con_cols))
     46#define LIN_TO_ROW(ti, lpos) ((lpos) / ((ti)->con_cols))
     47
    4448/** Seek direction */
    4549typedef enum {
     
    6165static void tinput_post_seek(tinput_t *, bool);
    6266
     67static void tinput_console_set_lpos(tinput_t *ti, unsigned lpos)
     68{
     69        console_set_pos(ti->console, LIN_TO_COL(ti, lpos),
     70            LIN_TO_ROW(ti, lpos));
     71}
     72
    6373/** Create a new text input field. */
    6474tinput_t *tinput_new(void)
     
    6676        tinput_t *ti;
    6777       
    68         ti = malloc(sizeof(tinput_t));
     78        ti = calloc(1, sizeof(tinput_t));
    6979        if (ti == NULL)
    7080                return NULL;
     
    7787void tinput_destroy(tinput_t *ti)
    7888{
     89        if (ti->prompt != NULL)
     90                free(ti->prompt);
    7991        free(ti);
     92}
     93
     94static void tinput_display_prompt(tinput_t *ti)
     95{
     96        tinput_console_set_lpos(ti, ti->prompt_coord);
     97
     98        console_set_style(ti->console, STYLE_EMPHASIS);
     99        printf("%s", ti->prompt);
     100        console_flush(ti->console);
     101        console_set_style(ti->console, STYLE_NORMAL);
    80102}
    81103
     
    88110        tinput_sel_get_bounds(ti, &sa, &sb);
    89111       
    90         console_set_pos(ti->console, (ti->col0 + start) % ti->con_cols,
    91             ti->row0 + (ti->col0 + start) / ti->con_cols);
     112        tinput_console_set_lpos(ti, ti->text_coord + start);
    92113        console_set_style(ti->console, STYLE_NORMAL);
    93114       
     
    134155static void tinput_position_caret(tinput_t *ti)
    135156{
    136         console_set_pos(ti->console, (ti->col0 + ti->pos) % ti->con_cols,
    137             ti->row0 + (ti->col0 + ti->pos) / ti->con_cols);
    138 }
    139 
    140 /** Update row0 in case the screen could have scrolled. */
     157        tinput_console_set_lpos(ti, ti->text_coord + ti->pos);
     158}
     159
     160/** Update text_coord, prompt_coord in case the screen could have scrolled. */
    141161static void tinput_update_origin(tinput_t *ti)
    142162{
    143         sysarg_t width = ti->col0 + ti->nc;
    144         sysarg_t rows = (width / ti->con_cols) + 1;
    145        
    146         /* Update row0 if the screen scrolled. */
    147         if (ti->row0 + rows > ti->con_rows)
    148                 ti->row0 = ti->con_rows - rows;
     163        unsigned end_coord = ti->text_coord + ti->nc;
     164        unsigned end_row = LIN_TO_ROW(ti, end_coord);
     165
     166        unsigned scroll_rows;
     167
     168        /* Update coords if the screen scrolled. */
     169        if (end_row >= ti->con_rows) {
     170                scroll_rows = end_row - ti->con_rows + 1;
     171                ti->text_coord -= ti->con_cols * scroll_rows;
     172                ti->prompt_coord -= ti->con_cols * scroll_rows;
     173        }
     174}
     175
     176static void tinput_jump_after(tinput_t *ti)
     177{
     178        tinput_console_set_lpos(ti, ti->text_coord + ti->nc);
     179        console_flush(ti->console);
     180        putchar('\n');
     181}
     182
     183static int tinput_display(tinput_t *ti)
     184{
     185        sysarg_t col0, row0;
     186       
     187        if (console_get_pos(ti->console, &col0, &row0) != EOK)
     188                return EIO;
     189       
     190        ti->prompt_coord = row0 * ti->con_cols + col0;
     191        ti->text_coord = ti->prompt_coord + str_length(ti->prompt);
     192
     193        tinput_display_prompt(ti);
     194        tinput_display_tail(ti, 0, 0);
     195        tinput_position_caret(ti);
     196
     197        return EOK;
    149198}
    150199
     
    154203                return;
    155204       
    156         sysarg_t new_width = ti->col0 + ti->nc + 1;
     205        unsigned new_width = LIN_TO_COL(ti, ti->text_coord) + ti->nc + 1;
    157206        if (new_width % ti->con_cols == 0) {
    158207                /* Advancing to new line. */
     
    185234                return;
    186235       
    187         sysarg_t new_width = ti->col0 + ti->nc + ilen;
    188         sysarg_t new_height = (new_width / ti->con_cols) + 1;
     236        unsigned new_width = LIN_TO_COL(ti, ti->text_coord) + ti->nc + ilen;
     237        unsigned new_height = (new_width / ti->con_cols) + 1;
    189238        if (new_height >= ti->con_rows) {
    190239                /* Disallow text longer than 1 page for now. */
     
    511560}
    512561
     562/** Compare two entries in array of completions. */
     563static int compl_cmp(void *va, void *vb, void *arg)
     564{
     565        const char *a = *(const char **) va;
     566        const char *b = *(const char **) vb;
     567
     568        return str_cmp(a, b);
     569}
     570
     571static size_t common_pref_len(const char *a, const char *b)
     572{
     573        size_t i;
     574        size_t a_off, b_off;
     575        wchar_t ca, cb;
     576
     577        i = 0;
     578        a_off = 0;
     579        b_off = 0;
     580
     581        while (true) {
     582                ca = str_decode(a, &a_off, STR_NO_LIMIT);
     583                cb = str_decode(b, &b_off, STR_NO_LIMIT);
     584
     585                if (ca == '\0' || cb == '\0' || ca != cb)
     586                        break;
     587                ++i;
     588        }
     589
     590        return i;
     591}
     592
     593static void tinput_text_complete(tinput_t *ti)
     594{
     595        void *state;
     596        size_t cstart;
     597        char *ctmp;
     598        char **compl;           /* Array of completions */
     599        size_t compl_len;       /* Current length of @c compl array */
     600        size_t cnum;
     601        size_t i;
     602        int rc;
     603
     604        if (ti->compl_ops == NULL)
     605                return;
     606
     607        /*
     608         * Obtain list of all possible completions (growing array).
     609         */
     610
     611        rc = (*ti->compl_ops->init)(ti->buffer, ti->pos, &cstart, &state);
     612        if (rc != EOK)
     613                return;
     614
     615        cnum = 0;
     616
     617        compl_len = 1;
     618        compl = malloc(compl_len * sizeof(char *));
     619        if (compl == NULL) {
     620                printf("Error: Out of memory.\n");
     621                return;
     622        }
     623
     624        while (true) {
     625                rc = (*ti->compl_ops->get_next)(state, &ctmp);
     626                if (rc != EOK)
     627                        break;
     628
     629                if (cnum >= compl_len) {
     630                        /* Extend array */
     631                        compl_len = 2 * compl_len;
     632                        compl = realloc(compl, compl_len * sizeof(char *));
     633                        if (compl == NULL) {
     634                                printf("Error: Out of memory.\n");
     635                                break;
     636                        }
     637                }
     638
     639                compl[cnum] = str_dup(ctmp);
     640                if (compl[cnum] == NULL) {
     641                        printf("Error: Out of memory.\n");
     642                        break;
     643                }
     644                cnum++;
     645        }
     646
     647        (*ti->compl_ops->fini)(state);
     648
     649        if (cnum > 1) {
     650                /*
     651                 * More than one match. Determine maximum common prefix.
     652                 */
     653                size_t cplen;
     654
     655                cplen = str_length(compl[0]);
     656                for (i = 1; i < cnum; i++)
     657                        cplen = min(cplen, common_pref_len(compl[0], compl[i]));
     658
     659                /* Compute how many bytes we should skip. */
     660                size_t istart = str_lsize(compl[0], ti->pos - cstart);
     661
     662                if (cplen > istart) {
     663                        /* Insert common prefix. */
     664
     665                        /* Copy remainder of common prefix. */
     666                        char *cpref = str_ndup(compl[0] + istart,
     667                            str_lsize(compl[0], cplen - istart));
     668
     669                        /* Insert it. */
     670                        tinput_insert_string(ti, cpref);
     671                        free(cpref);
     672                } else {
     673                        /* No common prefix. Sort and display all entries. */
     674
     675                        qsort(compl, cnum, sizeof(char *), compl_cmp, NULL);
     676
     677                        tinput_jump_after(ti);
     678                        for (i = 0; i < cnum; i++)
     679                                printf("%s\n", compl[i]);
     680                        tinput_display(ti);
     681                }
     682        } else if (cnum == 1) {
     683                /*
     684                 * We have exactly one match. Insert it.
     685                 */
     686
     687                /* Compute how many bytes of completion string we should skip. */
     688                size_t istart = str_lsize(compl[0], ti->pos - cstart);
     689
     690                /* Insert remainder of completion string at current position. */
     691                tinput_insert_string(ti, compl[0] + istart);
     692        }
     693
     694        for (i = 0; i < cnum; i++)
     695                free(compl[i]);
     696        free(compl);
     697}
     698
    513699/** Initialize text input field.
    514700 *
     
    521707        ti->hpos = 0;
    522708        ti->history[0] = NULL;
     709}
     710
     711/** Set prompt string.
     712 *
     713 * @param ti            Text input
     714 * @param prompt        Prompt string
     715 *
     716 * @return              EOK on success, ENOMEM if out of memory.
     717 */
     718int tinput_set_prompt(tinput_t *ti, const char *prompt)
     719{
     720        if (ti->prompt != NULL)
     721                free(ti->prompt);
     722       
     723        ti->prompt = str_dup(prompt);
     724        if (ti->prompt == NULL)
     725                return ENOMEM;
     726       
     727        return EOK;
     728}
     729
     730/** Set completion ops.
     731 *
     732 * Set pointer to completion ops structure that will be used for text
     733 * completion.
     734 */
     735void tinput_set_compl_ops(tinput_t *ti, tinput_compl_ops_t *compl_ops)
     736{
     737        ti->compl_ops = compl_ops;
    523738}
    524739
     
    539754                return EIO;
    540755       
    541         if (console_get_pos(ti->console, &ti->col0, &ti->row0) != EOK)
    542                 return EIO;
    543        
    544756        ti->pos = 0;
    545757        ti->sel_start = 0;
     
    549761        ti->exit_clui = false;
    550762       
     763        if (tinput_display(ti) != EOK)
     764                return EIO;
     765       
    551766        while (!ti->done) {
    552767                console_flush(ti->console);
     
    714929                tinput_history_seek(ti, -1);
    715930                break;
     931        case KC_TAB:
     932                tinput_text_complete(ti);
     933                break;
    716934        default:
    717935                break;
  • uspace/lib/clui/tinput.h

    r026793d r9be9c4d  
    11/*
    2  * Copyright (c) 2010 Jiri Svoboda
     2 * Copyright (c) 2011 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    3737#define LIBCLUI_TINPUT_H_
    3838
    39 #include <stdio.h>
     39#include <adt/list.h>
    4040#include <async.h>
    4141#include <inttypes.h>
    4242#include <io/console.h>
     43#include <stdio.h>
    4344
    4445#define HISTORY_LEN     10
    4546#define INPUT_MAX_SIZE  1024
     47
     48/** Begin enumeration of text completions.
     49 *
     50 * When user requests text completion, tinput will call this function to start
     51 * text completion operation. @a *cstart should be set to the position
     52 * (character index) of the first character of the 'word' that is being
     53 * completed. The resulting text is obtained by replacing the range of text
     54 * starting at @a *cstart and ending at @a pos with the completion text.
     55 *
     56 * The function can pass information to the get_next and fini functions
     57 * via @a state. The init function allocates the state object and stores
     58 * a pointer to it to @a *state. The fini function destroys the state object.
     59 *
     60 * @param text          Current contents of edit buffer (null-terminated).
     61 * @param pos           Current caret position.
     62 * @param cstart        Output, position in text where completion begins from.
     63 * @param state         Output, pointer to a client state object.
     64 *
     65 * @return              EOK on success, negative error code on failure.
     66 */
     67typedef int (*tinput_compl_init_fn)(wchar_t *text, size_t pos, size_t *cstart,
     68    void **state);
     69
     70/** Obtain one text completion alternative.
     71 *
     72 * Upon success the function sets @a *compl to point to a string, the
     73 * completion text. The caller (Tinput) should not modify or free the text.
     74 * The pointer is only valid until the next invocation of any completion
     75 * function.
     76 *
     77 * @param state         Pointer to state object created by the init funtion.
     78 * @param compl         Output, the completion text, ownership retained.
     79 *
     80 * @return              EOK on success, negative error code on failure.
     81 */
     82typedef int (*tinput_compl_get_next_fn)(void *state, char **compl);
     83
     84
     85/** Finish enumeration of text completions.
     86 *
     87 * The function must deallocate any state information allocated by the init
     88 * function or temporary data allocated by the get_next function.
     89 *
     90 * @param state         Pointer to state object created by the init funtion.
     91 */
     92typedef void (*tinput_compl_fini_fn)(void *state);
     93
     94/** Text completion ops. */
     95typedef struct {
     96        tinput_compl_init_fn init;
     97        tinput_compl_get_next_fn get_next;
     98        tinput_compl_fini_fn fini;
     99} tinput_compl_ops_t;
    46100
    47101/** Text input field (command line).
     
    53107        console_ctrl_t *console;
    54108       
     109        /** Prompt string */
     110        char *prompt;
     111       
     112        /** Completion ops. */
     113        tinput_compl_ops_t *compl_ops;
     114       
    55115        /** Buffer holding text currently being edited */
    56116        wchar_t buffer[INPUT_MAX_SIZE + 1];
    57117       
    58         /** Screen coordinates of the top-left corner of the text field */
    59         sysarg_t col0;
    60         sysarg_t row0;
     118        /** Linear position on screen where the prompt starts */
     119        unsigned prompt_coord;
     120        /** Linear position on screen where the text field starts */
     121        unsigned text_coord;
    61122       
    62123        /** Screen dimensions */
     
    90151
    91152extern tinput_t *tinput_new(void);
     153extern int tinput_set_prompt(tinput_t *, const char *);
     154extern void tinput_set_compl_ops(tinput_t *, tinput_compl_ops_t *);
    92155extern void tinput_destroy(tinput_t *);
    93156extern int tinput_read(tinput_t *, char **);
Note: See TracChangeset for help on using the changeset viewer.