msh.c 13.4 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 40
#ifdef RT_USING_DFS
#include <dfs_posix.h>
#endif

#define RT_FINSH_ARG_MAX    10
41
typedef int (*cmd_function_t)(int argc, char **argv);
B
Bernard Xiong 已提交
42 43

#ifdef FINSH_USING_MSH
B
bernard 已提交
44 45 46
#ifdef FINSH_USING_MSH_ONLY
rt_bool_t msh_is_used(void)
{
47
    return RT_TRUE;
B
bernard 已提交
48 49
}
#else
B
Bernard Xiong 已提交
50 51 52 53 54 55 56
#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)
{
57
    return __msh_state;
B
Bernard Xiong 已提交
58 59
}

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

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

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

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

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

98
    return 0;
B
Bernard Xiong 已提交
99
}
100
FINSH_FUNCTION_EXPORT_ALIAS(msh_help, __cmd_help, RT-Thread shell help.);
B
Bernard Xiong 已提交
101

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

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

    while (position < length)
    {
        /* strip bank and tab */
        while ((*ptr == ' ' || *ptr == '\t') && position < length)
        {
            *ptr = '\0';
            ptr ++; position ++;
        }
120 121 122 123 124 125 126 127 128 129 130 131

        if(argc >= RT_FINSH_ARG_MAX)
        {
            rt_kprintf("Too many args ! We only Use:\n");
            for(i = 0; i < argc; i++)
            {
                rt_kprintf("%s ", argv[i]);
            }
            rt_kprintf("\n");
            break;
        }

132 133 134 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
        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 已提交
170 171
}

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

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

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

    return cmd_func;
B
Bernard Xiong 已提交
192 193
}

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

G
Grissiom 已提交
204 205 206 207 208
    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 ++;
209 210

    /* get name length */
211
    length = cmd_length + 32;
212

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

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

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

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

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

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

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

    rt_free(pg_name);
G
Grissiom 已提交
263
    return ret;
264
}
265 266 267

int system(const char *command)
{
268 269 270 271 272 273 274 275 276 277
    int ret = -RT_ENOMEM;
    char *cmd = rt_strdup(command);

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

    return ret;
278
}
B
Bernard Xiong 已提交
279
RTM_EXPORT(system);
280 281
#endif

282
static int _msh_exec_cmd(char *cmd, rt_size_t length, int *retp)
B
Bernard Xiong 已提交
283
{
284
    int argc;
285
    rt_size_t cmd0_size = 0;
286
    cmd_function_t cmd_func;
G
Grissiom 已提交
287
    char *argv[RT_FINSH_ARG_MAX];
B
Bernard Xiong 已提交
288

G
Grissiom 已提交
289 290 291 292
    RT_ASSERT(cmd);
    RT_ASSERT(retp);

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

R
roamboy 已提交
298 299
    cmd_func = msh_get_cmd(cmd, cmd0_size);
    if (cmd_func == RT_NULL)
G
Grissiom 已提交
300
        return -RT_ERROR;
301

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

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

313
int msh_exec(char *cmd, rt_size_t length)
G
Grissiom 已提交
314 315 316
{
    int cmd_ret;

317 318
    /* strim the beginning of command */
    while (*cmd  == ' ' || *cmd == '\t')
G
Grissiom 已提交
319 320 321 322 323
    {
        cmd++;
        length--;
    }

G
Grissiom 已提交
324 325 326
    if (length == 0)
        return 0;

G
Grissiom 已提交
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
    /* Exec sequence:
     * 1. built-in command
     * 2. module(if enabled)
     * 3. chdir to the directry(if possible)
     */
    if (_msh_exec_cmd(cmd, length, &cmd_ret) == 0)
    {
        return cmd_ret;
    }
#ifdef RT_USING_MODULE
    if (msh_exec_module(cmd, length) == 0)
    {
        return 0;
    }
#endif
342 343

#if defined(RT_USING_DFS) && defined(DFS_USING_WORKDIR)
344 345 346 347 348 349
    if (msh_exec_script(cmd, length) == 0)
    {
        return 0;
    }

    /* change to this directory */
G
Grissiom 已提交
350 351 352 353 354
    if (chdir(cmd) == 0)
    {
        return 0;
    }
#endif
355

G
Grissiom 已提交
356 357 358 359
    /* truncate the cmd at the first space. */
    {
        char *tcmd;
        tcmd = cmd;
360
        while (*tcmd != ' ' && *tcmd != '\0')
G
Grissiom 已提交
361 362 363 364 365 366 367
        {
            tcmd++;
        }
        *tcmd = '\0';
    }
    rt_kprintf("%s: command not found.\n", cmd);
    return -1;
B
Bernard Xiong 已提交
368 369 370 371
}

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

374 375 376 377 378
    while ((*str != 0) && (*str2 != 0) && (*str == *str2))
    {
        str ++;
        str2 ++;
    }
B
Bernard Xiong 已提交
379

380
    return (str - str1);
B
Bernard Xiong 已提交
381 382
}

383 384 385
#ifdef RT_USING_DFS
void msh_auto_complete_path(char *path)
{
386
    DIR *dir = RT_NULL;
387
    struct dirent *dirent = RT_NULL;
388 389
    char *full_path, *ptr, *index;

390 391 392
    if (!path)
        return;

393
    full_path = (char *)rt_malloc(256);
394 395
    if (full_path == RT_NULL) return; /* out of memory */

G
Grissiom 已提交
396
    if (*path != '/')
397 398 399 400 401 402 403
    {
        getcwd(full_path, 256);
        if (full_path[rt_strlen(full_path) - 1]  != '/')
            strcat(full_path, "/");
    }
    else *full_path = '\0';

G
Grissiom 已提交
404 405
    index = RT_NULL;
    ptr = path;
406 407
    for (;;)
    {
408 409 410 411
        if (*ptr == '/') index = ptr + 1; 
        if (!*ptr) break; 

        ptr ++;
412 413 414 415 416 417 418 419
    }
    if (index == RT_NULL) index = path;

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

        /* fill the parent path */
G
Grissiom 已提交
420
        ptr = full_path;
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
        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 已提交
445

446 447 448 449 450
            rt_kprintf("%s\n", dirent->d_name);
        }
    }
    else
    {
451
        rt_size_t length, min_length;
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467

        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);
                }
468

469
                length = str_common(dirent->d_name, full_path);
470

471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
                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);
                }
            }
494

495 496 497 498 499 500 501 502
            length = index - path;
            memcpy(index, full_path, min_length);
            path[length + min_length] = '\0';
        }
    }

    closedir(dir);
    rt_free(full_path);
503 504 505
}
#endif

B
Bernard Xiong 已提交
506 507
void msh_auto_complete(char *prefix)
{
508 509 510
    int length, min_length;
    const char *name_ptr, *cmd_name;
    struct finsh_syscall *index;
B
Bernard Xiong 已提交
511

512 513
    min_length = 0;
    name_ptr = RT_NULL;
B
Bernard Xiong 已提交
514

515
    if (*prefix == '\0')
516 517 518 519
    {
        msh_help(0, RT_NULL);
        return;
    }
B
Bernard Xiong 已提交
520

521
#ifdef RT_USING_DFS
522 523 524 525 526 527 528 529 530 531 532 533
    /* 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 已提交
534

535 536
            ptr --;
        }
G
Grissiom 已提交
537 538 539 540 541 542 543 544 545
#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
546
    }
547
#endif
G
Grissiom 已提交
548

549 550 551 552 553 554 555
    /* 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;

556
            cmd_name = (const char *) &index->name[6];
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
            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 已提交
583 584
}
#endif
585