msh.c 14.3 KB
Newer Older
B
Bernard Xiong 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/*
 *  RT-Thread module shell implementation.
 *
 * COPYRIGHT (C) 2013, Shanghai Real-Thread Technology Co., Ltd
 *
 *  This file is part of RT-Thread (http://www.rt-thread.org)
 *  Maintainer: bernard.xiong <bernard.xiong at gmail.com>
 *
 *  All rights reserved.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Change Logs:
 * Date           Author       Notes
27 28
 * 2013-03-30     Bernard      the first verion for finsh
 * 2014-01-03     Bernard      msh can execute module.
29
 * 2017-07-19     Aubr.Cool    limit argc to RT_FINSH_ARG_MAX
B
Bernard Xiong 已提交
30 31 32 33 34 35
 */

#include "msh.h"
#include <finsh.h>
#include <shell.h>

36 37 38 39
#ifdef RT_USING_DFS
#include <dfs_posix.h>
#endif

40 41 42 43
#ifndef FINSH_ARG_MAX
#define FINSH_ARG_MAX    10
#endif

44
typedef int (*cmd_function_t)(int argc, char **argv);
B
Bernard Xiong 已提交
45 46

#ifdef FINSH_USING_MSH
B
bernard 已提交
47 48 49
#ifdef FINSH_USING_MSH_ONLY
rt_bool_t msh_is_used(void)
{
50
    return RT_TRUE;
B
bernard 已提交
51 52
}
#else
B
Bernard Xiong 已提交
53 54 55 56 57 58 59
#ifdef FINSH_USING_MSH_DEFAULT
static rt_bool_t __msh_state = RT_TRUE;
#else
static rt_bool_t __msh_state = RT_FALSE;
#endif
rt_bool_t msh_is_used(void)
{
60
    return __msh_state;
B
Bernard Xiong 已提交
61 62
}

63
static int msh_exit(int argc, char **argv)
B
Bernard Xiong 已提交
64
{
65 66
    /* return to finsh shell mode */
    __msh_state = RT_FALSE;
B
Bernard Xiong 已提交
67

68
    return 0;
B
Bernard Xiong 已提交
69 70 71 72 73
}
FINSH_FUNCTION_EXPORT_ALIAS(msh_exit, __cmd_exit, return to RT-Thread shell mode.);

static int msh_enter(void)
{
74 75 76
    /* enter module shell mode */
    __msh_state = RT_TRUE;
    return 0;
B
Bernard Xiong 已提交
77 78
}
FINSH_FUNCTION_EXPORT_ALIAS(msh_enter, msh, use module shell);
B
bernard 已提交
79
#endif
B
Bernard Xiong 已提交
80

81
int msh_help(int argc, char **argv)
B
Bernard Xiong 已提交
82
{
83 84 85 86 87
    rt_kprintf("RT-Thread shell commands:\n");
    {
        struct finsh_syscall *index;

        for (index = _syscall_table_begin;
88 89
                index < _syscall_table_end;
                FINSH_NEXT_SYSCALL(index))
90 91
        {
            if (strncmp(index->name, "__cmd_", 6) != 0) continue;
92
#if defined(FINSH_USING_DESCRIPTION) && defined(FINSH_USING_SYMTAB)
93
            rt_kprintf("%-16s - %s\n", &index->name[6], index->desc);
94
#else
95
            rt_kprintf("%s ", &index->name[6]);
96
#endif
97 98 99
        }
    }
    rt_kprintf("\n");
B
Bernard Xiong 已提交
100

101
    return 0;
B
Bernard Xiong 已提交
102
}
103
FINSH_FUNCTION_EXPORT_ALIAS(msh_help, __cmd_help, RT-Thread shell help.);
B
Bernard Xiong 已提交
104

105
static int msh_split(char *cmd, rt_size_t length, char *argv[FINSH_ARG_MAX])
B
Bernard Xiong 已提交
106
{
107 108 109
    char *ptr;
    rt_size_t position;
    rt_size_t argc;
110
    rt_size_t i;
111 112 113 114 115 116 117 118 119 120 121 122

    ptr = cmd;
    position = 0; argc = 0;

    while (position < length)
    {
        /* strip bank and tab */
        while ((*ptr == ' ' || *ptr == '\t') && position < length)
        {
            *ptr = '\0';
            ptr ++; position ++;
        }
123

124
        if(argc >= FINSH_ARG_MAX)
125 126 127 128 129 130 131 132 133 134
        {
            rt_kprintf("Too many args ! We only Use:\n");
            for(i = 0; i < argc; i++)
            {
                rt_kprintf("%s ", argv[i]);
            }
            rt_kprintf("\n");
            break;
        }

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
        if (position >= length) break;

        /* handle string */
        if (*ptr == '"')
        {
            ptr ++; position ++;
            argv[argc] = ptr; argc ++;

            /* skip this string */
            while (*ptr != '"' && position < length)
            {
                if (*ptr == '\\')
                {
                    if (*(ptr + 1) == '"')
                    {
                        ptr ++; position ++;
                    }
                }
                ptr ++; position ++;
            }
            if (position >= length) break;

            /* skip '"' */
            *ptr = '\0'; ptr ++; position ++;
        }
        else
        {
            argv[argc] = ptr;
            argc ++;
            while ((*ptr != ' ' && *ptr != '\t') && position < length)
            {
                ptr ++; position ++;
            }
            if (position >= length) break;
        }
    }

    return argc;
B
Bernard Xiong 已提交
173 174
}

175
static cmd_function_t msh_get_cmd(char *cmd, int size)
B
Bernard Xiong 已提交
176
{
177 178 179 180
    struct finsh_syscall *index;
    cmd_function_t cmd_func = RT_NULL;

    for (index = _syscall_table_begin;
181 182
            index < _syscall_table_end;
            FINSH_NEXT_SYSCALL(index))
183 184
    {
        if (strncmp(index->name, "__cmd_", 6) != 0) continue;
185

186
        if (strncmp(&index->name[6], cmd, size) == 0 &&
187
                index->name[6 + size] == '\0')
188 189 190 191 192 193 194
        {
            cmd_func = (cmd_function_t)index->func;
            break;
        }
    }

    return cmd_func;
B
Bernard Xiong 已提交
195 196
}

197
#if defined(RT_USING_MODULE) && defined(RT_USING_DFS)
G
Grissiom 已提交
198 199
/* Return 0 on module executed. Other value indicate error.
 */
200
int msh_exec_module(const char *cmd_line, int size)
201
{
G
Grissiom 已提交
202
    int ret;
203 204
    int fd = -1;
    char *pg_name;
205
    int length, cmd_length = 0;
206

G
Grissiom 已提交
207 208 209 210 211
    if (size == 0)
        return -RT_ERROR;
    /* get the length of command0 */
    while ((cmd_line[cmd_length] != ' ' && cmd_line[cmd_length] != '\t') && cmd_length < size)
        cmd_length ++;
212 213

    /* get name length */
214
    length = cmd_length + 32;
215

G
Grissiom 已提交
216
    /* allocate program name memory */
217
    pg_name = (char *) rt_malloc(length);
G
Grissiom 已提交
218 219
    if (pg_name == RT_NULL)
        return -RT_ENOMEM;
220

G
Grissiom 已提交
221 222 223
    /* copy command0 */
    memcpy(pg_name, cmd_line, cmd_length);
    pg_name[cmd_length] = '\0';
224 225

    if (strstr(pg_name, ".mo") != RT_NULL || strstr(pg_name, ".MO") != RT_NULL)
226 227
    {
        /* try to open program */
G
Grissiom 已提交
228
        fd = open(pg_name, O_RDONLY, 0);
229 230 231 232

        /* search in /bin path */
        if (fd < 0)
        {
233
            rt_snprintf(pg_name, length - 1, "/bin/%.*s", cmd_length, cmd_line);
234 235 236 237 238 239 240 241
            fd = open(pg_name, O_RDONLY, 0);
        }
    }
    else
    {
        /* add .mo and open program */

        /* try to open program */
G
Grissiom 已提交
242 243
        strcat(pg_name, ".mo");
        fd = open(pg_name, O_RDONLY, 0);
244 245 246 247

        /* search in /bin path */
        if (fd < 0)
        {
G
Grissiom 已提交
248
            rt_snprintf(pg_name, length - 1, "/bin/%.*s.mo", cmd_length, cmd_line);
249 250 251
            fd = open(pg_name, O_RDONLY, 0);
        }
    }
252

253 254 255 256
    if (fd >= 0)
    {
        /* found program */
        close(fd);
257
        rt_module_exec_cmd(pg_name, cmd_line, size);
G
Grissiom 已提交
258
        ret = 0;
259 260 261
    }
    else
    {
G
Grissiom 已提交
262
        ret = -1;
263 264 265
    }

    rt_free(pg_name);
G
Grissiom 已提交
266
    return ret;
267
}
268 269 270

int system(const char *command)
{
271 272 273 274 275 276 277 278 279 280
    int ret = -RT_ENOMEM;
    char *cmd = rt_strdup(command);

    if (cmd)
    {
        ret = msh_exec(cmd, rt_strlen(cmd));
        rt_free(cmd);
    }

    return ret;
281
}
B
Bernard Xiong 已提交
282
RTM_EXPORT(system);
283 284
#endif

285
static int _msh_exec_cmd(char *cmd, rt_size_t length, int *retp)
B
Bernard Xiong 已提交
286
{
287
    int argc;
288
    rt_size_t cmd0_size = 0;
289
    cmd_function_t cmd_func;
290
    char *argv[FINSH_ARG_MAX];
B
Bernard Xiong 已提交
291

G
Grissiom 已提交
292 293 294 295
    RT_ASSERT(cmd);
    RT_ASSERT(retp);

    /* find the size of first command */
R
roamboy 已提交
296 297
    while ((cmd[cmd0_size] != ' ' && cmd[cmd0_size] != '\t') && cmd0_size < length)
        cmd0_size ++;
G
Grissiom 已提交
298 299
    if (cmd0_size == 0)
        return -RT_ERROR;
300

R
roamboy 已提交
301 302
    cmd_func = msh_get_cmd(cmd, cmd0_size);
    if (cmd_func == RT_NULL)
G
Grissiom 已提交
303
        return -RT_ERROR;
304

R
roamboy 已提交
305
    /* split arguments */
306 307
    memset(argv, 0x00, sizeof(argv));
    argc = msh_split(cmd, length, argv);
G
Grissiom 已提交
308 309
    if (argc == 0)
        return -RT_ERROR;
B
Bernard Xiong 已提交
310

311
    /* exec this command */
G
Grissiom 已提交
312 313 314 315
    *retp = cmd_func(argc, argv);
    return 0;
}

H
heyuanjie 已提交
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
#if defined(RT_USING_LWP) && defined(RT_USING_DFS)
static int _msh_exec_lwp(char *cmd, rt_size_t length)
{
    int argc;
    int cmd0_size = 0;
    char *argv[FINSH_ARG_MAX];
    int fd = -1;
    char *pg_name;

    extern int exec(char*, int, char**);

    /* find the size of first command */
    while ((cmd[cmd0_size] != ' ' && cmd[cmd0_size] != '\t') && cmd0_size < length)
        cmd0_size ++;
    if (cmd0_size == 0)
        return -1;

    /* split arguments */
    rt_memset(argv, 0x00, sizeof(argv));
    argc = msh_split(cmd, length, argv);
    if (argc == 0)
        return -1;

    pg_name = argv[0];
    /* try to open program */
    fd = open(pg_name, O_RDONLY, 0);

    if (fd < 0)
        return -1;

    /* found program */
    close(fd);
    exec(pg_name, argc, argv);

    return 0;
}
#endif

354
int msh_exec(char *cmd, rt_size_t length)
G
Grissiom 已提交
355 356 357
{
    int cmd_ret;

358 359
    /* strim the beginning of command */
    while (*cmd  == ' ' || *cmd == '\t')
G
Grissiom 已提交
360 361 362 363 364
    {
        cmd++;
        length--;
    }

G
Grissiom 已提交
365 366 367
    if (length == 0)
        return 0;

G
Grissiom 已提交
368 369 370 371 372 373 374 375
    /* Exec sequence:
     * 1. built-in command
     * 2. module(if enabled)
     */
    if (_msh_exec_cmd(cmd, length, &cmd_ret) == 0)
    {
        return cmd_ret;
    }
376
#if defined(RT_USING_MODULE) && defined(RT_USING_DFS)
G
Grissiom 已提交
377 378 379 380 381
    if (msh_exec_module(cmd, length) == 0)
    {
        return 0;
    }
#endif
382 383

#if defined(RT_USING_DFS) && defined(DFS_USING_WORKDIR)
384 385 386 387
    if (msh_exec_script(cmd, length) == 0)
    {
        return 0;
    }
G
Grissiom 已提交
388
#endif
389

H
heyuanjie 已提交
390 391 392 393 394 395 396
#if defined(RT_USING_LWP) && defined(RT_USING_DFS)
    if (_msh_exec_lwp(cmd, length) == 0)
    {
        return 0;
    }
#endif

G
Grissiom 已提交
397 398 399 400
    /* truncate the cmd at the first space. */
    {
        char *tcmd;
        tcmd = cmd;
401
        while (*tcmd != ' ' && *tcmd != '\0')
G
Grissiom 已提交
402 403 404 405 406 407 408
        {
            tcmd++;
        }
        *tcmd = '\0';
    }
    rt_kprintf("%s: command not found.\n", cmd);
    return -1;
B
Bernard Xiong 已提交
409 410 411 412
}

static int str_common(const char *str1, const char *str2)
{
413
    const char *str = str1;
B
Bernard Xiong 已提交
414

415 416 417 418 419
    while ((*str != 0) && (*str2 != 0) && (*str == *str2))
    {
        str ++;
        str2 ++;
    }
B
Bernard Xiong 已提交
420

421
    return (str - str1);
B
Bernard Xiong 已提交
422 423
}

424 425 426
#ifdef RT_USING_DFS
void msh_auto_complete_path(char *path)
{
427
    DIR *dir = RT_NULL;
428
    struct dirent *dirent = RT_NULL;
429 430
    char *full_path, *ptr, *index;

431 432 433
    if (!path)
        return;

434
    full_path = (char *)rt_malloc(256);
435 436
    if (full_path == RT_NULL) return; /* out of memory */

G
Grissiom 已提交
437
    if (*path != '/')
438 439 440 441 442 443 444
    {
        getcwd(full_path, 256);
        if (full_path[rt_strlen(full_path) - 1]  != '/')
            strcat(full_path, "/");
    }
    else *full_path = '\0';

G
Grissiom 已提交
445 446
    index = RT_NULL;
    ptr = path;
447 448
    for (;;)
    {
449 450 451 452
        if (*ptr == '/') index = ptr + 1; 
        if (!*ptr) break; 

        ptr ++;
453 454 455 456 457 458 459 460
    }
    if (index == RT_NULL) index = path;

    if (index != RT_NULL)
    {
        char *dest = index;

        /* fill the parent path */
G
Grissiom 已提交
461
        ptr = full_path;
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
        while (*ptr) ptr ++;

        for (index = path; index != dest;)
            *ptr++ = *index++;
        *ptr = '\0';

        dir = opendir(full_path);
        if (dir == RT_NULL) /* open directory failed! */
        {
            rt_free(full_path);
            return;
        }

        /* restore the index position */
        index = dest;
    }

    /* auto complete the file or directory name */
    if (*index == '\0') /* display all of files and directories */
    {
        for (;;)
        {
            dirent = readdir(dir);
            if (dirent == RT_NULL) break;
G
Grissiom 已提交
486

487 488 489 490 491
            rt_kprintf("%s\n", dirent->d_name);
        }
    }
    else
    {
492
        rt_size_t length, min_length;
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508

        min_length = 0;
        for (;;)
        {
            dirent = readdir(dir);
            if (dirent == RT_NULL) break;

            /* matched the prefix string */
            if (strncmp(index, dirent->d_name, rt_strlen(index)) == 0)
            {
                if (min_length == 0)
                {
                    min_length = rt_strlen(dirent->d_name);
                    /* save dirent name */
                    strcpy(full_path, dirent->d_name);
                }
509

510
                length = str_common(dirent->d_name, full_path);
511

512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
                if (length < min_length)
                {
                    min_length = length;
                }
            }
        }

        if (min_length)
        {
            if (min_length < rt_strlen(full_path))
            {
                /* list the candidate */
                rewinddir(dir);

                for (;;)
                {
                    dirent = readdir(dir);
                    if (dirent == RT_NULL) break;

                    if (strncmp(index, dirent->d_name, rt_strlen(index)) == 0)
                        rt_kprintf("%s\n", dirent->d_name);
                }
            }
535

536 537 538 539 540 541 542 543
            length = index - path;
            memcpy(index, full_path, min_length);
            path[length + min_length] = '\0';
        }
    }

    closedir(dir);
    rt_free(full_path);
544 545 546
}
#endif

B
Bernard Xiong 已提交
547 548
void msh_auto_complete(char *prefix)
{
549 550 551
    int length, min_length;
    const char *name_ptr, *cmd_name;
    struct finsh_syscall *index;
B
Bernard Xiong 已提交
552

553 554
    min_length = 0;
    name_ptr = RT_NULL;
B
Bernard Xiong 已提交
555

556
    if (*prefix == '\0')
557 558 559 560
    {
        msh_help(0, RT_NULL);
        return;
    }
B
Bernard Xiong 已提交
561

562
#ifdef RT_USING_DFS
563 564 565 566 567 568 569 570 571 572 573 574
    /* check whether a spare in the command */
    {
        char *ptr;

        ptr = prefix + rt_strlen(prefix);
        while (ptr != prefix)
        {
            if (*ptr == ' ')
            {
                msh_auto_complete_path(ptr + 1);
                break;
            }
G
Grissiom 已提交
575

576 577
            ptr --;
        }
G
Grissiom 已提交
578 579 580 581 582 583 584 585 586
#ifdef RT_USING_MODULE
        /* There is a chance that the user want to run the module directly. So
         * try to complete the file names. If the completed path is not a
         * module, the system won't crash anyway. */
        if (ptr == prefix)
        {
            msh_auto_complete_path(ptr);
        }
#endif
587
    }
588
#endif
G
Grissiom 已提交
589

590 591 592 593 594 595 596
    /* checks in internal command */
    {
        for (index = _syscall_table_begin; index < _syscall_table_end; FINSH_NEXT_SYSCALL(index))
        {
            /* skip finsh shell function */
            if (strncmp(index->name, "__cmd_", 6) != 0) continue;

597
            cmd_name = (const char *) &index->name[6];
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
            if (strncmp(prefix, cmd_name, strlen(prefix)) == 0)
            {
                if (min_length == 0)
                {
                    /* set name_ptr */
                    name_ptr = cmd_name;
                    /* set initial length */
                    min_length = strlen(name_ptr);
                }

                length = str_common(name_ptr, cmd_name);
                if (length < min_length)
                    min_length = length;

                rt_kprintf("%s\n", cmd_name);
            }
        }
    }

    /* auto complete string */
    if (name_ptr != NULL)
    {
        rt_strncpy(prefix, name_ptr, min_length);
    }

    return ;
B
Bernard Xiong 已提交
624 625
}
#endif
626