新建项目

This commit is contained in:
gitea committed 2023-04-29 09:09:12 +08:00
1 parent 5e2e890cfc
commit 633d9b872b
1351 files changed
+730564

No files matched your search

@@ -0,0 +1,17 @@
# cpp suport
![standard](https://img.shields.io/badge/standard-c99-brightgreen.svg)
![build](https://img.shields.io/badge/build-2021.01.17-brightgreen.svg)
![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)
`letter shell`cpp支持
## 简介
`cpp support`是用于扩展`letter shell`,使其可以在cpp环境中生效的组件,`cpp support`仅仅包含一个头文件
## 使用
`cpp support`仅仅提供了一个`shell_cpp.h`的头文件,用于在cpp文件中使用`letter shell`的命令导出,在使用时,需要在`.cpp`文件中包含`shell_cpp.h`,而不是`shell.h`,如果一个工程同时包含`.c`和`.cpp`文件,只需要分别包含对应的头文件即可
注意,对于cpp,`letter shell`也仅支持函数的导出,不适用于类的成员函数
@@ -0,0 +1,155 @@
/**
* @file shell_cpp.h
* @author Letter (nevermindzzt@gmail.com)
* @brief shell cpp support
* @version 1.0.0
* @date 2021-01-09
*
* @copyright (c) 2021 Letter
*
*/
#ifndef __SHELL_CPP_H__
#define __SHELL_CPP_H__
#ifdef __cplusplus
extern "C" {
#include "shell.h"
/**
* @brief shell command cpp 支持 cmd 定义
*/
typedef struct shell_command_cpp_cmd
{
int attr; /**< 属性 */
const char *name; /**< 命令名 */
int (*function)(); /**< 命令执行函数 */
const char *desc; /**< 命令描述 */
} ShellCommandCppCmd;
/**
* @brief shell command cpp 支持 var 定义
*/
typedef struct shell_command_cpp_var
{
int attr; /**< 属性 */
const char *name; /**< 变量名 */
void *value; /**< 变量值 */
const char *desc; /**< 变量描述 */
} ShellCommandCppVar;
/**
* @brief shell command cpp 支持 user 定义
*/
typedef struct shell_command_cpp_user
{
int attr; /**< 属性 */
const char *name; /**< 用户名 */
const char *password; /**< 用户密码 */
const char *desc; /**< 用户描述 */
} ShellCommandCppUser;
/**
* @brief shell command cpp 支持 key 定义
*/
typedef struct shell_command_cpp_key
{
int attr; /**< 属性 */
int value; /**< 按键键值 */
void (*function)(Shell *); /**< 按键执行函数 */
const char *desc; /**< 按键描述 */
} ShellCommandCppKey;
#if SHELL_USING_CMD_EXPORT == 1
#undef SHELL_EXPORT_CMD
/**
* @brief shell 命令定义
*
* @param _attr 命令属性
* @param _name 命令名
* @param _func 命令函数
* @param _desc 命令描述
*/
#define SHELL_EXPORT_CMD(_attr, _name, _func, _desc) \
const char shellCmd##_name[] = #_name; \
const char shellDesc##_name[] = #_desc; \
extern "C" SHELL_USED const ShellCommandCppCmd \
shellCommand##_name SHELL_SECTION("shellCommand") = \
{ \
_attr, \
shellCmd##_name, \
(int (*)())_func, \
shellDesc##_name \
}
#undef SHELL_EXPORT_VAR
/**
* @brief shell 变量定义
*
* @param _attr 变量属性
* @param _name 变量名
* @param _value 变量值
* @param _desc 变量描述
*/
#define SHELL_EXPORT_VAR(_attr, _name, _value, _desc) \
const char shellCmd##_name[] = #_name; \
const char shellDesc##_name[] = #_desc; \
extern "C" SHELL_USED const ShellCommandCppVar \
shellVar##_name SHELL_SECTION("shellCommand") = \
{ \
_attr, \
shellCmd##_name, \
(void *)_value, \
shellDesc##_name \
}
#undef SHELL_EXPORT_USER
/**
* @brief shell 用户定义
*
* @param _attr 用户属性
* @param _name 用户名
* @param _password 用户密码
* @param _desc 用户描述
*/
#define SHELL_EXPORT_USER(_attr, _name, _password, _desc) \
const char shellCmd##_name[] = #_name; \
const char shellPassword##_name[] = #_password; \
const char shellDesc##_name[] = #_desc; \
extern "C" SHELL_USED const ShellCommandCppUser \
shellUser##_name SHELL_SECTION("shellCommand") = \
{ \
_attr|SHELL_CMD_TYPE(SHELL_TYPE_USER), \
shellCmd##_name, \
shellPassword##_name, \
shellDesc##_name \
}
#undef SHELL_EXPORT_KEY
/**
* @brief shell 按键定义
*
* @param _attr 按键属性
* @param _value 按键键值
* @param _func 按键函数
* @param _desc 按键描述
*/
#define SHELL_EXPORT_KEY(_attr, _value, _func, _desc) \
const char shellDesc##_value[] = #_desc; \
extern "C" SHELL_USED const ShellCommandCppKey \
shellKey##_value SHELL_SECTION("shellCommand") = \
{ \
_attr|SHELL_CMD_TYPE(SHELL_TYPE_KEY), \
_value, \
(void (*)(Shell *))_func, \
shellDesc##_value \
}
#endif /** SHELL_USING_CMD_EXPORT == 1 */
}
#endif /**< defined __cplusplus */
#endif /**< __SHELL_CPP_H__ */
@@ -0,0 +1,52 @@
# letter shell file system support
![version](https://img.shields.io/badge/version-1.0.0-brightgreen.svg)
![standard](https://img.shields.io/badge/standard-c99-brightgreen.svg)
![build](https://img.shields.io/badge/build-2020.08.16-brightgreen.svg)
![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)
letter shell文件系统支持
![preview](../../doc/img/fs_support_preview.gif)
- [letter shell file system support](#letter-shell-file-system-support)
- [简介](#简介)
- [使用](#使用)
## 简介
fs_support作为letter shell的插件,用于实现letter shell对常见文件系统操作的支持,比如说cd,ls等命令,fs_support依赖于letter shell的伴生对象功能,并且使用到了内存分配和内存释放,所以请确认已经配置好了letter shell
fs_support并非一个完全实现的letter shell插件,由于文件系统的接口和操作系统以及具体使用的文件系统相关,所以fs_support仅仅通过接入几个基本的接口以实现`cd`,`ls`命令,具体使用时,可能需要根据使用的文件系统接口修改fs_support,letter shell的[demo/x86-gcc](demo/x86-gcc)下有针对linux平台的移植,可以及进行参考
## 使用
1. 声明`ShellFs`对象并申请当前路径数据缓冲区
```c
ShellFs shellFs;
char shellPathBuffer[512] = "/";
```
2. 实现`getcwd`,`chdir`,`listdir`函数
根据文件系统不同,这些函数会有不同的实现,请根据具体使用的环境进行修改
3. 初始化`ShellFs`对象
```c
shellFs.getcwd = getcwd;
shellFs.chdir = chdir;
shellFs.listdir = userShellListDir;
shellFsInit(&shellFs, shellPathBuffer, 512);
```
4. 初始化`Shell`对象
设置shell当前路径缓冲,初始化shell,并添加伴生对象
```C
shellSetPath(&shell, shellPathBuffer);
shellInit(&shell, shellBuffer, 512);
shellCompanionAdd(&shell, SHELL_COMPANION_ID_FS, &shellFs);
```
@@ -0,0 +1,74 @@
/**
* @file shell_fs.c
* @author Letter (nevermindzzt@gmail.com)
* @brief shell file system support
* @version 0.1
* @date 2020-07-22
*
* @copyright (c) 2020 Letter
*
*/
#include "shell_fs.h"
#include "shell.h"
#include "stdio.h"
/**
* @brief 改变当前路径(shell调用)
*
* @param dir 路径
*/
void shellCD(char *dir)
{
Shell *shell = shellGetCurrent();
ShellFs *shellFs = shellCompanionGet(shell, SHELL_COMPANION_ID_FS);
SHELL_ASSERT(shellFs, return);
if (shellFs->chdir(dir) != 0)
{
shellWriteString(shell, "error: ");
shellWriteString(shell, dir);
shellWriteString(shell, " is not a directory\r\n");
}
shellFs->getcwd(shellFs->info.path, shellFs->info.pathLen);
}
SHELL_EXPORT_CMD(
SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
cd, shellCD, change dir);
/**
* @brief 列出文件(shell调用)
*
*/
void shellLS(void)
{
size_t count;
char *buffer;
Shell *shell = shellGetCurrent();
ShellFs *shellFs = shellCompanionGet(shell, SHELL_COMPANION_ID_FS);
SHELL_ASSERT(shellFs, return);
buffer = SHELL_MALLOC(SHELL_FS_LIST_FILE_BUFFER_MAX);
SHELL_ASSERT(buffer, return);
count = shellFs->listdir(shellGetPath(shell), buffer, SHELL_FS_LIST_FILE_BUFFER_MAX);
shellWriteString(shell, buffer);
SHELL_FREE(buffer);
}
SHELL_EXPORT_CMD(
SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
ls, shellLS, list all files);
/**
* @brief 初始化shell文件系统支持
*
* @param shellFs shell文件系统对象
* @param pathBuffer shell路径缓冲
* @param pathLen 路径缓冲区大小
*/
void shellFsInit(ShellFs *shellFs, char *pathBuffer, size_t pathLen)
{
shellFs->info.path = pathBuffer;
shellFs->info.pathLen = pathLen;
shellFs->getcwd(shellFs->info.path, pathLen);
}
@@ -0,0 +1,41 @@
/**
* @file shell_fs.h
* @author Letter (nevermindzzt@gmail.com)
* @brief shell file system support
* @version 0.1
* @date 2020-07-22
*
* @copyright (c) 2020 Letter
*
*/
#ifndef __SHELL_FS_H__
#define __SHELL_FS_H__
#include "stddef.h"
#include "shell.h"
#define SHELL_FS_VERSION "1.0.0"
#define SHELL_COMPANION_ID_FS -1
#define SHELL_FS_LIST_FILE_BUFFER_MAX 4096
/**
* @brief shell文件系统支持结构体
*
*/
typedef struct shell_fs
{
size_t (*getcwd)(char *, size_t);
size_t (*chdir)(char *);
size_t (*listdir)(char *dir, char *buffer, size_t maxLen);
struct {
char *path;
size_t pathLen;
} info;
} ShellFs;
void shellFsInit(ShellFs *shellFs, char *pathBuffer, size_t pathLen);
#endif
@@ -0,0 +1,423 @@
/*
============================================================================
Name : 2048.c
Author : Maurits van der Schee
Description : Console version of the game "2048" for GNU/Linux
============================================================================
*/
#define _XOPEN_SOURCE 500
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include "shell.h"
#define printf(...) shellPrint(shell2048, ##__VA_ARGS__)
#define getchar() shellGetChar(shell2048)
#define SIZE 4
uint32_t score=0;
uint8_t scheme=0;
Shell *shell2048 = NULL;
char shellGetChar(Shell *shell) {
char data;
if (shell->read && shell->read(&data, 1) == 1) {
return data;
} else {
return -1;
}
}
void sleep(int ms) {
int time = SHELL_GET_TICK();
while (SHELL_GET_TICK() - time < ms) ;
}
void getColor(uint8_t value, char *color, size_t length) {
uint8_t original[] = {8,255,1,255,2,255,3,255,4,255,5,255,6,255,7,255,9,0,10,0,11,0,12,0,13,0,14,0,255,0,255,0};
uint8_t blackwhite[] = {232,255,234,255,236,255,238,255,240,255,242,255,244,255,246,0,248,0,249,0,250,0,251,0,252,0,253,0,254,0,255,0};
uint8_t bluered[] = {235,255,63,255,57,255,93,255,129,255,165,255,201,255,200,255,199,255,198,255,197,255,196,255,196,255,196,255,196,255,196,255};
uint8_t *schemes[] = {original,blackwhite,bluered};
uint8_t *background = schemes[scheme]+0;
uint8_t *foreground = schemes[scheme]+1;
if (value > 0) while (value--) {
if (background+2<schemes[scheme]+sizeof(original)) {
background+=2;
foreground+=2;
}
}
snprintf(color,length,"\033[38;5;%d;48;5;%dm",*foreground,*background);
}
void drawBoard(uint8_t board[SIZE][SIZE]) {
uint8_t x,y;
char color[40], reset[] = "\033[m";
printf("\033[H");
printf("2048.c %17d pts\n\n",score);
for (y=0;y<SIZE;y++) {
for (x=0;x<SIZE;x++) {
getColor(board[x][y],color,40);
printf("%s",color);
printf(" ");
printf("%s",reset);
}
printf("\n");
for (x=0;x<SIZE;x++) {
getColor(board[x][y],color,40);
printf("%s",color);
if (board[x][y]!=0) {
char s[8];
snprintf(s,8,"%u",(uint32_t)1<<board[x][y]);
uint8_t t = 7-strlen(s);
printf("%*s%s%*s",t-t/2,"",s,t/2,"");
} else {
printf(" · ");
}
printf("%s",reset);
}
printf("\n");
for (x=0;x<SIZE;x++) {
getColor(board[x][y],color,40);
printf("%s",color);
printf(" ");
printf("%s",reset);
}
printf("\n");
}
printf("\n");
printf(" ←,↑,→,↓ or q \n");
printf("\033[A"); // one line up
}
uint8_t findTarget(uint8_t array[SIZE],uint8_t x,uint8_t stop) {
uint8_t t;
// if the position is already on the first, don't evaluate
if (x==0) {
return x;
}
for(t=x-1;;t--) {
if (array[t]!=0) {
if (array[t]!=array[x]) {
// merge is not possible, take next position
return t+1;
}
return t;
} else {
// we should not slide further, return this one
if (t==stop) {
return t;
}
}
}
// we did not find a
return x;
}
bool slideArray(uint8_t array[SIZE]) {
bool success = false;
uint8_t x,t,stop=0;
for (x=0;x<SIZE;x++) {
if (array[x]!=0) {
t = findTarget(array,x,stop);
// if target is not original position, then move or merge
if (t!=x) {
// if target is zero, this is a move
if (array[t]==0) {
array[t]=array[x];
} else if (array[t]==array[x]) {
// merge (increase power of two)
array[t]++;
// increase score
score+=(uint32_t)1<<array[t];
// set stop to avoid double merge
stop = t+1;
}
array[x]=0;
success = true;
}
}
}
return success;
}
void rotateBoard(uint8_t board[SIZE][SIZE]) {
uint8_t i,j,n=SIZE;
uint8_t tmp;
for (i=0; i<n/2; i++) {
for (j=i; j<n-i-1; j++) {
tmp = board[i][j];
board[i][j] = board[j][n-i-1];
board[j][n-i-1] = board[n-i-1][n-j-1];
board[n-i-1][n-j-1] = board[n-j-1][i];
board[n-j-1][i] = tmp;
}
}
}
bool moveUp(uint8_t board[SIZE][SIZE]) {
bool success = false;
uint8_t x;
for (x=0;x<SIZE;x++) {
success |= slideArray(board[x]);
}
return success;
}
bool moveLeft(uint8_t board[SIZE][SIZE]) {
bool success;
rotateBoard(board);
success = moveUp(board);
rotateBoard(board);
rotateBoard(board);
rotateBoard(board);
return success;
}
bool moveDown(uint8_t board[SIZE][SIZE]) {
bool success;
rotateBoard(board);
rotateBoard(board);
success = moveUp(board);
rotateBoard(board);
rotateBoard(board);
return success;
}
bool moveRight(uint8_t board[SIZE][SIZE]) {
bool success;
rotateBoard(board);
rotateBoard(board);
rotateBoard(board);
success = moveUp(board);
rotateBoard(board);
return success;
}
bool findPairDown(uint8_t board[SIZE][SIZE]) {
bool success = false;
uint8_t x,y;
for (x=0;x<SIZE;x++) {
for (y=0;y<SIZE-1;y++) {
if (board[x][y]==board[x][y+1]) return true;
}
}
return success;
}
uint8_t countEmpty(uint8_t board[SIZE][SIZE]) {
uint8_t x,y;
uint8_t count=0;
for (x=0;x<SIZE;x++) {
for (y=0;y<SIZE;y++) {
if (board[x][y]==0) {
count++;
}
}
}
return count;
}
bool gameEnded(uint8_t board[SIZE][SIZE]) {
bool ended = true;
if (countEmpty(board)>0) return false;
if (findPairDown(board)) return false;
rotateBoard(board);
if (findPairDown(board)) ended = false;
rotateBoard(board);
rotateBoard(board);
rotateBoard(board);
return ended;
}
void addRandom(uint8_t board[SIZE][SIZE]) {
static bool initialized = false;
uint8_t x,y;
uint8_t r,len=0;
uint8_t n,list[SIZE*SIZE][2];
if (!initialized) {
srand(SHELL_GET_TICK());
initialized = true;
}
for (x=0;x<SIZE;x++) {
for (y=0;y<SIZE;y++) {
if (board[x][y]==0) {
list[len][0]=x;
list[len][1]=y;
len++;
}
}
}
if (len>0) {
r = rand()%len;
x = list[r][0];
y = list[r][1];
n = (rand()%10)/9+1;
board[x][y]=n;
}
}
void initBoard(uint8_t board[SIZE][SIZE]) {
uint8_t x,y;
for (x=0;x<SIZE;x++) {
for (y=0;y<SIZE;y++) {
board[x][y]=0;
}
}
addRandom(board);
addRandom(board);
drawBoard(board);
score = 0;
}
// int test() {
// uint8_t array[SIZE];
// // these are exponents with base 2 (1=2 2=4 3=8)
// uint8_t data[] = {
// 0,0,0,1, 1,0,0,0,
// 0,0,1,1, 2,0,0,0,
// 0,1,0,1, 2,0,0,0,
// 1,0,0,1, 2,0,0,0,
// 1,0,1,0, 2,0,0,0,
// 1,1,1,0, 2,1,0,0,
// 1,0,1,1, 2,1,0,0,
// 1,1,0,1, 2,1,0,0,
// 1,1,1,1, 2,2,0,0,
// 2,2,1,1, 3,2,0,0,
// 1,1,2,2, 2,3,0,0,
// 3,0,1,1, 3,2,0,0,
// 2,0,1,1, 2,2,0,0
// };
// uint8_t *in,*out;
// uint8_t t,tests;
// uint8_t i;
// bool success = true;
// tests = (sizeof(data)/sizeof(data[0]))/(2*SIZE);
// for (t=0;t<tests;t++) {
// in = data+t*2*SIZE;
// out = in + SIZE;
// for (i=0;i<SIZE;i++) {
// array[i] = in[i];
// }
// slideArray(array);
// for (i=0;i<SIZE;i++) {
// if (array[i] != out[i]) {
// success = false;
// }
// }
// if (success==false) {
// for (i=0;i<SIZE;i++) {
// printf("%d ",in[i]);
// }
// printf("=> ");
// for (i=0;i<SIZE;i++) {
// printf("%d ",array[i]);
// }
// printf("expected ");
// for (i=0;i<SIZE;i++) {
// printf("%d ",in[i]);
// }
// printf("=> ");
// for (i=0;i<SIZE;i++) {
// printf("%d ",out[i]);
// }
// printf("\n");
// break;
// }
// }
// if (success) {
// printf("All %u tests executed successfully\n",tests);
// }
// return !success;
// }
int main_2048(int argc, char *argv[]) {
uint8_t board[SIZE][SIZE];
char c;
bool success;
shell2048 = shellGetCurrent();
if (!shell2048) {
return -1;
}
// if (argc == 2 && strcmp(argv[1],"test")==0) {
// return test();
// }
if (argc == 2 && strcmp(argv[1],"blackwhite")==0) {
scheme = 1;
}
if (argc == 2 && strcmp(argv[1],"bluered")==0) {
scheme = 2;
}
printf("\033[?25l\033[2J");
initBoard(board);
while (true) {
c=getchar();
if (c == -1){
printf("\nError! Cannot read keyboard input!");
break;
}
switch(c) {
case 97: // 'a' key
case 104: // 'h' key
case 68: // left arrow
success = moveLeft(board); break;
case 100: // 'd' key
case 108: // 'l' key
case 67: // right arrow
success = moveRight(board); break;
case 119: // 'w' key
case 107: // 'k' key
case 65: // up arrow
success = moveUp(board); break;
case 115: // 's' key
case 106: // 'j' key
case 66: // down arrow
success = moveDown(board); break;
default: success = false;
}
if (success) {
drawBoard(board);
sleep(150);
addRandom(board);
drawBoard(board);
if (gameEnded(board)) {
printf(" GAME OVER \n");
break;
}
}
if (c=='q') {
printf(" QUIT? (y/n) \n");
c=getchar();
if (c=='y') {
break;
}
drawBoard(board);
}
if (c=='r') {
printf(" RESTART? (y/n) \n");
c=getchar();
if (c=='y') {
initBoard(board);
}
drawBoard(board);
}
}
printf("\033[?25h\033[m");
return EXIT_SUCCESS;
}
@@ -0,0 +1,27 @@
/**
* @file game.c
* @author Letter
* @brief game
* @version 0.1
* @date 2021-07-18
*
* @copyright (c) 2021 Letter
*
*/
#include "shell_cmd_group.h"
extern int main_2048(int argc, char *argv[]);
extern int main_pushbox(int argc, char* argv[]);
ShellCommand gameGroup[] =
{
SHELL_CMD_GROUP_ITEM(SHELL_TYPE_CMD_MAIN, 2048, main_2048,
game 2048\n2048 [param]\nParam: blackwhite bluered or null),
SHELL_CMD_GROUP_ITEM(SHELL_TYPE_CMD_MAIN,
pushbox, main_pushbox, game pushbox),
SHELL_CMD_GROUP_END()
};
SHELL_EXPORT_CMD_GROUP(
SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN,
game, gameGroup, games);
@@ -0,0 +1,183 @@
#include "shell.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define printf(...) shellPrint(shellPushbox, ##__VA_ARGS__)
#define getchar() shellGetChar(shellPushbox)
Shell *shellPushbox = NULL;
static char shellGetChar(Shell *shell) {
char data;
if (shell->read && shell->read(&data, 1) == 1) {
return data;
} else {
return -1;
}
}
//地图7行8列 行[0-6] 列[0-7]
//0 路
//1 墙
//2 箱子
//3 终点
//4 小老鼠
//7 小老鼠站在终止上
//5 箱子到达终点上
int g_boards[7][8] = {
{ 0, 1, 1, 1, 1, 1, 1, 0 },
{ 0, 1, 0, 0, 0, 0, 1, 1 },
{ 1, 3, 0, 1, 1, 2, 0, 1 },
{ 1, 0, 3, 3, 2, 0, 0, 1 },
{ 1, 0, 0, 1, 2, 0, 0, 1 },
{ 1, 0, 0, 4, 0, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 0, 0, 0 }
},
boards[7][8] = {};
//记录小老鼠在移动中的位置
int row = 0;
int col = 0;
int cnt = 0; //箱子个数,用来判断游戏是否结束。
//初始化地图
void pushbox_init()
{
for (int i = 0; i < 7 * 8; i++) { //这里是为了当你按下enter键时,能够重置游戏,重置游戏时需要重新初始化地图
boards[i / 8][i % 8] = g_boards[i / 8][i % 8];
if (4 == boards[i / 8][i % 8]) //这里还需要记录老鼠的位置
{
row = i / 8;
col = i % 8;
}
}
}
//打印地图
void pushbox_print_map()
{
printf( "\r\n LetterShell PushBox!\r\n");
for (int i = 0; i < 7; i++) {
printf( " ");
for (int j = 0; j < 8; j++) {
switch (boards[i][j]) {
case 0:
printf( " ");
break; //空格代表路径
case 1:
printf( "■");
break; //打印墙
case 2:
case 5:
printf( "●");
break; //打印箱子
case 3:
printf( "★");
break; //打印终点
case 4:
case 7:
printf( "♀");
break; //打印小老鼠
}
}
printf( "\r\n");
}
printf("w,a,s,d or q\n");
}
//推箱子
int pushbox_print_move(int nrow, int ncol, int nnrow, int nncol)
{
printf( "\033[H");
if (0 == boards[nrow][ncol] || 3 == boards[nrow][ncol]) //如果小老鼠前面是路或者终点,说明可以移动
{
boards[nrow][ncol] += 4; //小老鼠进入这个位置了
boards[row][col] -= 4; //小老鼠离开这个位置了
//移动过后小老鼠的位置也改变了
row = nrow;
col = ncol;
} else if (2 == boards[nrow][ncol] || 5 == boards[nrow][ncol]) //如果小老鼠前面是箱子
{
if (0 == boards[nnrow][nncol] || 3 == boards[nnrow][nncol]) //如果箱子前面是路或者终点则可以移动
{
boards[nnrow][nncol] += 2;
boards[nrow][ncol] -= 2 - 4;
//这里其实是boards[nrow][ncol]=boards[nrow][ncol]-2+4
//减去2是因为箱子移走了,加上4是因为小老鼠进入了
boards[row][col] -= 4;
row = nrow;
col = ncol;
}
}
for (int i = 0; i < 7; i++) //统计游戏是否结束,当箱子都在终点上时就结束了
{
for (int j = 0; j < 8; j++) {
if (5 == boards[i][j]) {
cnt++;
}
if (3 == cnt) {
printf( "通关!\r\n");
return 1; //通关退出游戏
}
}
}
cnt = 0; //如果游戏没有结束,下次还是需要从0统计
pushbox_print_map();
return 0;
}
int main_pushbox(int argc, char* argv[])
{
char c;
int res = 0;
shellPushbox = shellGetCurrent();
if (!shellPushbox) {
return -1;
}
printf( "\033[?25l\033[2J");
printf( "\033[H");
pushbox_init();//游戏初始化
pushbox_print_map();
while (1) {
c = getchar();;
if (c < 0) {
printf("\nError! Cannot read keyboard input!");
break;
}
switch (c) //每次都需要判断小老鼠前面和前面的前面的坐标的状态,这样可以在move()
{ //函数中统一各个方向的写法
case 'w':
res = pushbox_print_move(row - 1, col, row - 2, col);
break;
case 's':
res = pushbox_print_move(row + 1, col, row + 2, col);
break;
case 'a':
res = pushbox_print_move(row, col - 1, row, col - 2);
break;
case 'd':
res = pushbox_print_move(row, col + 1, row, col + 2);
break;
case 'r':
pushbox_init();
break; //按enter则从新开始游戏
case 'q':
res = 1; //如果按q则退出游戏
}
if(res){
break;
}
}
printf( "\033[?25l\033[2J");
return EXIT_SUCCESS;
}
@@ -0,0 +1,14 @@
# game
![version](https://img.shields.io/badge/version-1.0.0-brightgreen.svg)
![standard](https://img.shields.io/badge/standard-c99-brightgreen.svg)
![build](https://img.shields.io/badge/build-2021.07.18-brightgreen.svg)
![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)
## 简介
这是一个仅供娱乐的组件,移植了几个命令行的游戏
| 游戏 | 来源 |
| ---- | ------------------------------------------------ |
| 2048 | [mevdschee/2048](https://github.com/mevdschee/2048.c) |
@@ -0,0 +1,309 @@
/**
* @file log.c
* @author Letter (nevermindzzt@gmail.com)
* @brief log
* @version 1.0.0
* @date 2020-07-30
*
* @copyright (c) 2020 Letter
*
*/
#include "log.h"
#include "stdio.h"
#include "stdarg.h"
#include "shell.h"
#if LOG_USING_COLOR == 1
#define memPrintHead CSI(31) \
" Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F" \
CSI(39) \
"\r\n"
#define memPrintAddr CSI(31)"0x%08x: "CSI(39)
#else
#define memPrintHead " Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n"
#define memPrintAddr "0x%08x: "
#endif
Log *logList[LOG_MAX_NUMBER] = {0};
static char logBuffer[LOG_BUFFER_SIZE];
#if LOG_USING_LOCK == 1
/**
* @brief 上锁log对象
* @param log log对象
*/
static void logLock(Log *log)
{
if (log == LOG_ALL_OBJ)
{
for (short i = 0; i < LOG_MAX_NUMBER; i++)
{
if (logList[i] && logList[i]->active)
{
if (logList[i]->lock)
{
LOG_LOCK(logList[i]);
}
}
}
}
else if (log->lock)
{
LOG_LOCK(log);
}
}
/**
* @brief 解锁log对象
* @param log log对象
*/
static void logUnlock(Log *log)
{
if (log == LOG_ALL_OBJ)
{
for (short i = 0; i < LOG_MAX_NUMBER; i++)
{
if (logList[i] && logList[i]->active)
{
if (logList[i]->unlock)
{
LOG_UNLOCK(logList[i]);
}
}
}
}
else if (log->unlock)
{
LOG_UNLOCK(log);
}
}
#endif /* LOG_USING_LOCK == 1 */
/**
* @brief 注册log对象
*
* @param log log对象
*/
void logRegister(Log *log, Shell *shell)
{
if (shell)
{
log->shell = shell;
#if SHELL_USING_COMPANION == 1
shellCompanionAdd(shell, SHELL_COMPANION_ID_LOG, log);
#endif
}
for (short i = 0; i < LOG_MAX_NUMBER; i++)
{
if (logList[i] == 0)
{
logList[i] = log;
return;
}
}
}
/**
* @brief 注销log对象
*
* @param log log对象
*/
void logUnRegister(Log *log)
{
for (short i = 0; i < LOG_MAX_NUMBER; i++)
{
if (logList[i] == log)
{
logList[i] = 0;
return;
}
}
}
/**
* @brief 设置log日志级别
*
* @param log log对象
* @param level 日志级别
*/
void logSetLevel(Log *log, LogLevel level)
{
logAssert(log, return);
log->level = level;
}
#if SHELL_USING_COMPANION == 1
SHELL_EXPORT_CMD_AGENCY(
SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
logSetLevel, logSetLevel, set log level\r\n logSetLevel [level],
(void *)shellCompanionGet(shellGetCurrent(), SHELL_COMPANION_ID_LOG), p1);
#else
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
logSetLevel, logSetLevel, set log level\r\n logSetLevel [log] [level]);
#endif /** SHELL_USING_COMPANION == 1 */
/**
* @brief log写buffer
*
* @param log log对象
* @param level 日志级别
* @param buffer buffer
* @param len buffer长度
*/
static void logWriteBuffer(Log *log, LogLevel level, char *buffer, short len)
{
#if LOG_USING_LOCK == 1
logLock(log);
#endif /* LOG_USING_LOCK == 1 */
if (log == LOG_ALL_OBJ)
{
for (short i = 0; i < LOG_MAX_NUMBER; i++)
{
if (logList[i]
&& logList[i]->active
&& logList[i]->level >= level)
{
logList[i]->write(logBuffer, len);
}
}
}
else if (log && log->active && log->level >= level)
{
log->write(logBuffer, len);
}
#if LOG_USING_LOCK == 1
logUnlock(log);
#endif /* LOG_USING_LOCK == 1 */
}
/**
* @brief log格式化写入数据
*
* @param log log对象
* @param level log级别
* @param fmt 格式
* @param ... 参数
*/
void logWrite(Log *log, LogLevel level, const char *fmt, ...)
{
va_list vargs;
short len;
#if LOG_USING_LOCK == 1
logLock(log);
#endif /* LOG_USING_LOCK == 1 */
va_start(vargs, fmt);
len = vsnprintf(logBuffer, LOG_BUFFER_SIZE - 1, fmt, vargs);
va_end(vargs);
logWriteBuffer(log, level, logBuffer, len);
#if LOG_USING_LOCK == 1
logUnlock(log);
#endif /* LOG_USING_LOCK == 1 */
}
/**
* @brief 16进制输出
*
* @param log log对象
* @param level 日志级别
* @param base 内存基址
* @param length 长度
*/
void logHexDump(Log *log, LogLevel level, void *base, unsigned int length)
{
unsigned char *address;
unsigned int len;
unsigned int printLen = 0;
if (length == 0 || (log != LOG_ALL_OBJ && log->level < level))
{
return;
}
#if LOG_USING_LOCK == 1
logLock(log);
#endif /* LOG_USING_LOCK == 1 */
len = snprintf(logBuffer, LOG_BUFFER_SIZE - 1, "memory of 0x%08x, size: %d:\r\n%s",
(unsigned int)base, length, memPrintHead);
logWriteBuffer(log, level, logBuffer, len);
len = length;
address = (unsigned char *)((unsigned int)base & (~0x0000000F));
length += (unsigned int)base - (unsigned int)address;
length = (length + 15) & (~0x0000000F);
while (length)
{
printLen += sprintf(logBuffer + printLen, memPrintAddr, (unsigned int)address);
for (int i = 0; i < 16; i++)
{
if ((unsigned int)(address + i) < (unsigned int)base
|| (unsigned int)(address + i) >= (unsigned int)base + len)
{
logBuffer[printLen ++] = ' ';
logBuffer[printLen ++] = ' ';
logBuffer[printLen ++] = ' ';
}
else
{
printLen += sprintf(logBuffer + printLen, "%02x ", *(address + i));
}
}
logBuffer[printLen ++] = '|';
logBuffer[printLen ++] = ' ';
for (int i = 0; i < 16; i++)
{
if ((unsigned int)(address + i) < (unsigned int)base
|| (unsigned int)(address + i) >= (unsigned int)base + len)
{
logBuffer[printLen ++] = ' ';
}
else
{
if (*(address + i) >= 32 && *(address + i) <= 126)
{
printLen += sprintf(logBuffer + printLen, "%c", *(address + i));
}
else
{
logBuffer[printLen ++] = '.';
}
}
}
logBuffer[printLen ++] = ' ';
logBuffer[printLen ++] = '|';
logBuffer[printLen ++] = '\r';
logBuffer[printLen ++] = '\n';
logWriteBuffer(log, level, logBuffer, printLen);
address += 16;
length -= 16;
printLen = 0;
}
#if LOG_USING_LOCK == 1
logUnlock(log);
#endif /* LOG_USING_LOCK == 1 */
}
#if SHELL_USING_COMPANION == 1
SHELL_EXPORT_CMD_AGENCY(
SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
hexdump, logHexDump, hex dump\r\n hexdump [base] [len],
(void *)shellCompanionGet(shellGetCurrent(), SHELL_COMPANION_ID_LOG), LOG_NONE, (void *)p1, (unsigned int)p2);
#else
SHELL_EXPORT_CMD(
SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
hexdump, logHexDump, hex dump\r\n hexdump [log] [level] [base] [len]);
#endif /** SHELL_USING_COMPANION == 1 */
#if SHELL_USING_COMPANION == 1
void logSwitchLevel(Shell *shell)
{
Log *log = shellCompanionGet(shell, SHELL_COMPANION_ID_LOG);
SHELL_ASSERT(log, return);
log->level = (LogLevel)(log->level >= LOG_ALL ? LOG_NONE : (log->level + 1));
logPrintln("set log level: %d", log->level);
}
SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x04000000, logSwitchLevel, switch log level);
#endif /** SHELL_USING_COMPANION == 1 */
@@ -0,0 +1,224 @@
/**
* @file log.h
* @author Letter (nevermindzzt@gmail.com)
* @brief log
* @version 1.0.0
* @date 2020-07-30
*
* @copyright (c) 2020 Letter
*
*/
#ifndef __LOG_H__
#define __LOG_H__
#ifdef __cplusplus
extern "C" {
#endif /**< defined __cplusplus */
#include "shell.h"
#define LOG_VERSION "1.0.1"
#define SHELL_COMPANION_ID_LOG -2
#define LOG_USING_LOCK 0
#define LOG_BUFFER_SIZE 256 /**< log输出缓冲大小 */
#define LOG_USING_COLOR 1 /**< 是否使用颜色 */
#define LOG_MAX_NUMBER 5 /**< 允许注册的最大log对象数量 */
#define LOG_AUTO_TAG 1 /**< 是否自动添加TAG */
#define LOG_END "\r\n" /**< log信息结尾 */
#define LOG_TIME_STAMP 0 /**< 设置获取系统时间戳 */
#ifndef LOG_TAG
#define LOG_TAG __FUNCTION__ /**< 自定添加的TAG */
#endif
#ifndef LOG_ENABLE
#define LOG_ENABLE 1 /**< 使能log */
#endif
#if LOG_USING_LOCK == 1
#define LOG_LOCK(log) log->lock(log)
#define LOG_UNLOCK(log) log->unlock(log)
#else
#define LOG_LOCK(log)
#define LOG_UNLOCK(log)
#endif /* LOG_USING_LOCK == 1 */
#define LOG_ALL_OBJ ((Log *)-1) /**< 所有已注册的log对象 */
/**
* 终端字体颜色代码
*/
#define CSI_BLACK 30 /**< 黑色 */
#define CSI_RED 31 /**< 红色 */
#define CSI_GREEN 32 /**< 绿色 */
#define CSI_YELLOW 33 /**< 黄色 */
#define CSI_BLUE 34 /**< 蓝色 */
#define CSI_FUCHSIN 35 /**< 品红 */
#define CSI_CYAN 36 /**< 青色 */
#define CSI_WHITE 37 /**< 白色 */
#define CSI_BLACK_L 90 /**< 亮黑 */
#define CSI_RED_L 91 /**< 亮红 */
#define CSI_GREEN_L 92 /**< 亮绿 */
#define CSI_YELLOW_L 93 /**< 亮黄 */
#define CSI_BLUE_L 94 /**< 亮蓝 */
#define CSI_FUCHSIN_L 95 /**< 亮品红 */
#define CSI_CYAN_L 96 /**< 亮青 */
#define CSI_WHITE_L 97 /**< 亮白 */
#define CSI_DEFAULT 39 /**< 默认 */
#define CSI(code) "\033[" #code "m" /**< ANSI CSI指令 */
/**
* log级别字符(包含颜色)
*/
#if LOG_USING_COLOR == 1
#define ERROR_TEXT CSI(31) "E(%d) %s:" CSI(39) /**< 错误标签 */
#define WARNING_TEXT CSI(33) "W(%d) %s:" CSI(39) /**< 警告标签 */
#define INFO_TEXT CSI(32) "I(%d) %s:" CSI(39) /**< 信息标签 */
#define DEBUG_TEXT CSI(34) "D(%d) %s:" CSI(39) /**< 调试标签 */
#define VERBOSE_TEXT CSI(36) "V(%d) %s:" CSI(39) /**< 冗余信息标签 */
#else
#define ERROR_TEXT "E(%d) %s:"
#define WARNING_TEXT "W(%d) %s:"
#define INFO_TEXT "I(%d) %s:"
#define DEBUG_TEXT "D(%d) %s:"
#define VERBOSE_TEXT "V(%d) %s:"
#endif
/**
* @brief 日志级别定义
*
*/
typedef enum
{
LOG_NONE = 0, /**< 无级别 */
LOG_ERROR = 1, /**< 错误 */
LOG_WRANING = 2, /**< 警告 */
LOG_INFO = 3, /**< 消息 */
LOG_DEBUG = 4, /**< 调试 */
LOG_VERBOSE = 5, /**< 冗余 */
LOG_ALL = 6, /**< 所有日志 */
} LogLevel;
/**
* @brief log对象定义
*
*/
typedef struct log_def
{
void (*write)(char *, short); /**< 写buffer */
char active; /**< 是否激活 */
LogLevel level; /**< 日志级别 */
#if LOG_USING_LOCK == 1
int (*lock)(struct log_def *); /**< log 加锁 */
int (*unlock)(struct log_def *); /**< log 解锁 */
#endif /** LOG_USING_LOCK == 1 */
Shell *shell; /**< 关联shell对象 */
} Log;
/**
* @brief log打印(自动换行)
*
* @param fmt 格式
* @param ... 参数
*/
#define logPrintln(format, ...) \
logWrite(LOG_ALL_OBJ, LOG_NONE, format "\r\n", ##__VA_ARGS__)
/**
* @brief 日志格式化输出
*
* @param text 消息文本
* @param level 日志级别
* @param fmt 格式
* @param ... 参数
*/
#define logFormat(text, level, fmt, ...) \
if (LOG_ENABLE) {\
logWrite(LOG_ALL_OBJ, level, text " " fmt "" LOG_END, \
LOG_TIME_STAMP, LOG_TAG, ##__VA_ARGS__); }
/**
* @brief 错误log输出
*
* @param fmt 格式
* @param ... 参数
*/
#define logError(fmt, ...) \
logFormat(ERROR_TEXT, LOG_ERROR, fmt, ##__VA_ARGS__)
/**
* @brief 警告log输出
*
* @param fmt 格式
* @param ... 参数
*/
#define logWarning(fmt, ...) \
logFormat(WARNING_TEXT, LOG_WRANING, fmt, ##__VA_ARGS__)
/**
* @brief 信息log输出
*
* @param fmt 格式
* @param ... 参数
*/
#define logInfo(fmt, ...) \
logFormat(INFO_TEXT, LOG_INFO, fmt, ##__VA_ARGS__)
/**
* @brief 调试log输出
*
* @param fmt 格式
* @param ... 参数
*/
#define logDebug(fmt, ...) \
logFormat(DEBUG_TEXT, LOG_DEBUG, fmt, ##__VA_ARGS__)
/**
* @brief 冗余log输出
*
* @param fmt 格式
* @param ... 参数
*/
#define logVerbose(fmt, ...) \
logFormat(VERBOSE_TEXT, LOG_VERBOSE, fmt, ##__VA_ARGS__)
/**
* @brief 断言
*
* @param expr 表达式
* @param action 断言失败操作
*/
#define logAssert(expr, action) \
if (!(expr)) { \
logError("\"" #expr "\" assert failed at file: %s, line: %d", __FILE__, __LINE__); \
action; \
}
/**
* @brief 16进制输出到所有终端
*
* @param base 内存基址
* @param length 长度
*/
#define logHexDumpAll(base, length) \
logHexDump(LOG_ALL_OBJ, LOG_ALL, base, length)
void logRegister(Log *log, Shell *shell);
void logUnRegister(Log *log);
void logSetLevel(Log *log, LogLevel level);
void logWrite(Log *log, LogLevel level, const char *fmt, ...);
void logHexDump(Log *log, LogLevel level, void *base, unsigned int length);
#ifdef __cplusplus
}
#endif /**< defined __cplusplus */
#endif
@@ -0,0 +1,215 @@
# log
![version](https://img.shields.io/badge/version-1.0.1-brightgreen.svg)
![standard](https://img.shields.io/badge/standard-c99-brightgreen.svg)
![build](https://img.shields.io/badge/build-2020.08.02-brightgreen.svg)
![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)
嵌入式日志打印工具
![preview](../../doc/img/log_preview.png)
- [log](#log)
- [简介](#简介)
- [使用](#使用)
- [配置](#配置)
- [API](#api)
- [logPrintln](#logprintln)
- [logError](#logerror)
- [logWarning](#logwarning)
- [logInfo](#loginfo)
- [logDebug](#logdebug)
- [logVerbose](#logverbose)
- [logAssert](#logassert)
- [logRegister](#logregister)
- [logSetLevel](#logsetlevel)
- [logHexDump](#loghexdump)
- [结合letter shell尾行模式](#结合letter-shell尾行模式)
- [其他用法](#其他用法)
- [单独控制某个文件日志](#单独控制某个文件日志)
## 简介
log是一个用于嵌入式系统的日志打印工具,可以为日志定义不同的级别,然后设置日志工具的打印级别,可以进行日志打印的控制
此外,log通过letter shell的伴生对象功能,可以和letter shell结合,实现log和shell的绑定等功能
## 使用
1. 实现log写buffer函数
```C
void uartLogWrite(char *buffer, short len)
{
serialTransmit(&debugSerial, (uint8_t *)buffer, len, 0x100);
}
```
2. 定义log对象
```C
Log uartLog = {
.write = uartLogWrite,
.active = true,
.level = LOG_DEBUG
};
```
3. 注册log对象
```C
logRegister(&uartLog, NULL);
```
## 配置
通过修改log.h文件中的宏,可以对log工具进行配置
| 宏 | 意义 |
| --------------- | ------------------------- |
| LOG_BUFFER_SIZE | log输出缓冲大小 |
| LOG_USING_COLOR | 是否使用颜色 |
| LOG_MAX_NUMBER | 允许注册的最大log对象数量 |
| LOG_AUTO_TAG | 是否自动添加TAG |
| LOG_END | log信息结尾 |
| LOG_TAG | 自定添加的TAG |
| LOG_TIME_STAMP | 设置获取系统时间戳 |
## API
以下是log工具部分API的说明
### logPrintln
宏声明,用于一般的打印输出
```C
#define logPrintln(format, ...)
```
- 参数
- `format` 输出格式
- `...` 可变参数
### logError
宏声明,错误日志级别输出
```C
#define logError(fmt, ...)
```
- 参数
- `fmt` 输出格式
- `...` 可变参数
### logWarning
宏声明,警告日志级别输出,函数原型及参数说明参考`logError`
### logInfo
宏声明,信息日志级别输出,函数原型及参数说明参考`logError`
### logDebug
宏声明,调试日志级别输出,函数原型及参数说明参考`logError`
### logVerbose
宏声明,冗余日志级别输出,函数原型及参数说明参考`logError`
### logAssert
宏声明,断言
```C
#define logAssert(expr, action)
```
- 参数
- `expr` 表达式
- `action` 断言失败执行操作
### logRegister
注册log对象
```C
void logRegister(Log *log, Shell *shell)
```
- 参数
- `log` log对象
- `shell` 关联的shell对象
### logSetLevel
设置日志级别
```C
void logSetLevel(Log *log, LogLevel level)
```
- 参数
- `log` log对象
- `level` 日志级别
### logHexDump
数据16进制打印
```C
void logHexDump(Log *log, LogLevel level, void *base, unsigned int length)
```
- 参数
- `log` log对象
- `level` 日志级别
- `base` 数据基址
- `length` 数据长度
## 结合letter shell尾行模式
log工具可以结合letter shell的尾行模式,实现log和shell共用一个终端,但不影响shell交互体验
1. 使用`shellWriteEndLine`事项log写buffer函数
```C
void uartLogWrite(char *buffer, short len)
{
if (uartLog.shell)
{
shellWriteEndLine(uartLog.shell, buffer, len);
}
}
```
2. 定义log对象
```C
Log uartLog = {
.write = uartLogWrite,
.active = true,
.level = LOG_DEBUG
};
```
3. 注册log对象
```C
logRegister(&uartLog, &shell);
```
## 其他用法
### 单独控制某个文件日志
log工具可以单独对某个文件的日志进行打印控制,使用时,在对应的.c文件中加入
```C
#undef LOG_ENABLE
#define LOG_ENABLE 1
```
即可单独控制某个文件的日志开关
@@ -0,0 +1,170 @@
# shell_enhance
![version](https://img.shields.io/badge/version-1.0.0-brightgreen.svg)
![standard](https://img.shields.io/badge/standard-c99-brightgreen.svg)
![build](https://img.shields.io/badge/build-2020.10.18-brightgreen.svg)
![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)
- [shell_enhance](#shell_enhance)
- [简介](#简介)
- [组件](#组件)
- [shell_cmd_group](#shell_cmd_group)
- [shell_passthrough](#shell_passthrough)
- [shell_secure_user](#shell_secure_user)
## 简介
`shell_enhance`是`letter shell 3.0`上用于增强shell功能的组件,`shell_enhance`分离了一些不常用的shell功能,对于需要这些功能的用户,只需要将对应的文件加入到编译系统中即可
## 组件
`shell_enhance`目前包含的组件如下:
| 组件 | 描述 | 依赖文件 |
| ----------------- | -------------- | --------------------------------------- |
| shell_cmd_group | 提供命令组功能 | shell_cmd_group.c shell_cmd_group.h |
| shell_passthrough | 提供透传功能 | shell_passthrough.c shell_passthrough.h |
| shell_secure_user | 安全用户功能 | shell_secure_user.c shell_secure_user.h |
### shell_cmd_group
`shell_cmd_group`提供了一个命令组的功能,用户可以将多个相关的命令打包成一个命令组,然后通过形如`cmdgroup subcmd [param]`的方式进行命令调用
- 定义命令数组
命令数组用于将多个命令关联到一个数组,这个数组就是定义命令组需要的,你需要想正常命令一样,为每个子命令对应定义一个函数,然后将他们添加到一个数组中,可以使用宏`SHELL_CMD_GROUP_ITEM`进行定义
```c
int test(int a, char *b)
{
printf("hello world, %d, %s\r\n", a, b);
return 0;
}
int test2(int argc, char *argv[])
{
printf("%d parameters\r\n", argc);
for (short i = 0; i < argc; i++)
{
printf("%s\t", argv[i]);
}
printf("\r\n");
}
ShellCommand testGroup[] =
{
SHELL_CMD_GROUP_ITEM(SHELL_TYPE_CMD_FUNC, test, test, command group test1),
SHELL_CMD_GROUP_ITEM(SHELL_TYPE_CMD_MAIN, test2, test2, command group test2),
SHELL_CMD_GROUP_END()
};
```
其中`SHELL_CMD_GROUP_END()`需要写在每个命令数组的最后
- 定义命令组
使用宏`SHELL_EXPORT_CMD_GROUP`定义命令组
```c
SHELL_EXPORT_CMD_GROUP(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), gt, testGroup, command group test);
```
- 调用
在命令行,使用`cmdgroup subcmd [param]`的形式调用命令
```sh
letter:/$ gt test 666 Letter
hello world, 666, Letter
Return: 0, 0x00000000
letter:/$ gt test2 hello world
3 parameters
test2 hello world
Return: 0, 0x00000000
```
- 命令帮助
通过使用`cmdgroup -h`的方式查看完整的命令帮助信息
```sh
letter:/$ gt -h
command group help of gt
test: command group test1
test2: command group test2
Return: 0, 0x00000000
```
### shell_passthrough
`shell_passthrough`提供了一个透传模式的功能,用户可以定义一个透传命令,执行透传命令后进入对应的透传模式,此后,命令行接收到的数据将以行为单位透传到定义的`handler`中,此扩展适用于一些需要进行终端数据转发的场景,常见的场景比如说通过命令行发送AT指令
- 定义hanadler
passthrough模式需要定义一个函数,用于处理命令行传过来的数据,函数原型为`typedef int (*ShellPassthrough)(char *data, unsigned short len)`
```C
void shellPassthroughTest(char *data, unsigned short len)
{
printf("passthrough mode test, data: %s, len: %d\r\n", data, len);
}
```
- 定义passthrough
使用宏`SHELL_EXPORT_PASSTROUGH`定义`passthrough`
```C
SHELL_EXPORT_PASSTROUGH(SHELL_CMD_PERMISSION(0), passTest, passthrough>>, shellPassthroughTest, passthrough mode test);
```
- 调用
直接在命令行输入定义的`passthrough`名字可进入对应的透传模式,之后在输入的数据会按照行为单位通过`handler`调用
```sh
letter:/mnt/f/Github/letter shell/demo/x86-gcc$ passTest
passthrough>>hello
passthrough mode test, data: hello, len: 5
```
- 退出
使用组合键`Ctrl + D`退出`passthrough`,键值可以在`shell_passthrough.h`文件中的`SHELL_PASSTHROUGH_EXIT_KEY`宏修改
- 单次调用
某些情况下,使用`passthrough`模式时,我们可能只需要单次数据的透传,此时可以不进入`passthrough`命令行,直接调用命令带上透传的数据即可
```sh
letter:/mnt/f/Github/letter shell/demo/x86-gcc$ passTest "hello world"
passthrough mode test, data: hello world, len: 11
```
### shell_secure_user
`shell_secure_user`组件是对shell用户的一个补充,在`letter shell`中,我们可以通过定义不同的用户和分配命令权限,约束使用者可以执行的命令,可以将高权限的用户定义密码,但是,shell用户的默认实现只支持固定的密码,在编译时就已经确定,安全性不高
使用`shell_secure_user`组件,我们可以定义一个函数,使用这个函数得到用户的密码,一般的,我们可以根据芯片的ID,mac地址,甚至可以向服务器做请求以获取密码,提高安全性
- 定义获取用户密码函数(handler)
示例函数直接返回用户名作为密码,实际使用时可以通过任何方式计算或者获取密码,注意,此处返回的密码字符串,请自行分配内存保存
```c
char *shellSecureUserHandlerTest(const char *name)
{
return (char *)name;
}
```
- 定义用户
```c
SHELL_EXPORT_SECURE_USER(SHELL_CMD_PERMISSION(0xFF), secure, shellSecureUserHandlerTest, secure user test);
```
- 调用
使用`shell_secure_user`定义的用户和shell默认用户调用方法完全一致,只需要在shell命令行输入用户名和密码即可
@@ -0,0 +1,82 @@
/**
* @file shell_cmd_group.c
* @author Letter(nevermindzzt@gmail.com)
* @brief shell command group support
* @version 0.1
* @date 2020-10-18
*
* @copyright (c) 2020 Letter
*
*/
#include "shell_cmd_group.h"
#include "string.h"
#include "stdio.h"
unsigned int shellRunCommand(Shell *shell, ShellCommand *command);
static void shellCmdGroupHelp(Shell *shell, char *name, ShellCommand *group);
/**
* @brief shell命令组运行
*
* @param group 命令数组
* @param argc 参数数量
* @param argv 参数
*
* @return int 执行命令返回值
*/
int shellCmdGroupRun(ShellCommand *group, int argc, char *argv[])
{
ShellCommand *item = group;
Shell *shell = shellGetCurrent();
SHELL_ASSERT(shell, return -1);
if (argc == 1 || strcmp("-h", argv[1]) == 0)
{
shellCmdGroupHelp(shell, argv[0], group);
return 0;
}
while (item->data.cmd.name)
{
if (strcmp(item->data.cmd.name, argv[1]) == 0)
{
for (short i = 0; i < argc - 1; i++)
{
argv[i] = argv[i + 1];
}
shell->parser.paramCount--;
return shellRunCommand(shell, item);
}
item++;
}
shellWriteString(shell, "sub command not found\r\n");
return -1;
}
/**
* @brief shell 命令组帮助
*
* @param shell shell对象
* @param name 命令组名
* @param group 命令数组
*/
static void shellCmdGroupHelp(Shell *shell, char *name, ShellCommand *group)
{
ShellCommand *item = group;
shellWriteString(shell, "command group help of ");
shellWriteString(shell, name);
shellWriteString(shell, "\r\n");
while (item->data.cmd.name)
{
shellWriteString(shell, item->data.cmd.name);
shellWriteString(shell, ": ");
if (item->data.cmd.desc) {
shellWriteString(shell, item->data.cmd.desc);
}
shellWriteString(shell, "\r\n");
item++;
}
}
@@ -0,0 +1,62 @@
/**
* @file shell_cmd_group.h
* @author Letter(nevermindzzt@gmail.com)
* @brief shell command group support
* @version 0.1
* @date 2020-10-18
*
* @copyright (c) 2020 Letter
*
*/
#include "shell.h"
/**
* @brief shell 命令组函数名
*/
#define SHELL_CMD_GROUP_FUNC_NAME(_group) agency##_group
/**
* @brief shell命令组函数定义
*
* @param _group 命令数组
*/
#define SHELL_CMD_GROUP_FUNC(_group) \
void SHELL_CMD_GROUP_FUNC_NAME(_group)(int p1, int p2) \
{ shellCmdGroupRun(_group, p1, (char **)p2); }
/**
* @brief shell 命令组定义
*
* @param _attr 属性
* @param _name 命令组名
* @param _group 命令数组
* @param _desc 命令组描述
*/
#define SHELL_EXPORT_CMD_GROUP(_attr, _name, _group, _desc) \
SHELL_CMD_GROUP_FUNC(_group) \
SHELL_EXPORT_CMD(_attr, _name, SHELL_CMD_GROUP_FUNC_NAME(_group), _desc)
/**
* @brief shell 命令组item定义
*
* @param _type 命令类型(SHELL_TYPE_CMD_MAIN or SHELL_TYPE_CMD_FUNC)
* @param _func 命令函数
* @param _desc 命令描述
*/
#define SHELL_CMD_GROUP_ITEM(_type, _name, _func, _desc) \
{ \
.attr.value = SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(_type)|SHELL_CMD_DISABLE_RETURN, \
.data.cmd.name = #_name, \
.data.cmd.function = (int (*)())_func, \
.data.cmd.desc = #_desc \
}
/**
* @brief shell 命令组定义结尾
*
* @note 需要添加在每个命令数组的最后一条
*/
#define SHELL_CMD_GROUP_END() {0}
int shellCmdGroupRun(ShellCommand *group, int argc, char *argv[]);
@@ -0,0 +1,68 @@
/**
* @file shell_passthrough.c
* @author Letter(nevermindzzt@gmail.com)
* @brief shell passthrough mode
* @version 0.1
* @date 2021-05-31
*
* @copyright (c) 2021 Letter
*
*/
#include "shell_passthrough.h"
#include "string.h"
/**
* @brief shell passthrough 模式
*
* @param shell shell
* @param prompt passthrough 提示符
* @param handler pssthrough handler
* @param argc 命令行参数数量
* @param argv 命令行参数
*
* @return unsigned int 返回值
*/
unsigned int shellPassthrough(Shell *shell, const char *prompt, ShellPassthrough handler, int argc, char *argv[])
{
char data;
if (argc > 1)
{
handler(argv[1], strlen(argv[1]));
}
else
{
shell->status.isActive = 0;
shellWriteString(shell, prompt);
while (1)
{
if (shell->read && shell->read(&data, 1) == 1)
{
if (data == '\r' || data == '\n')
{
shellWriteString(shell, "\r\n");
if (shell->parser.length != 0)
{
shell->parser.buffer[shell->parser.length] = 0;
handler(shell->parser.buffer, shell->parser.length);
shell->parser.length = 0;
shell->parser.cursor = 0;
}
shellWriteString(shell, prompt);
}
else if (data == SHELL_PASSTHROUGH_EXIT_KEY)
{
shellWriteString(shell, "\r\n");
shell->parser.length = 0;
shell->parser.cursor = 0;
return -1;
}
else
{
shellHandler(shell, data);
}
}
}
}
return 0;
}
@@ -0,0 +1,62 @@
/**
* @file shell_passthrough.h
* @author Letter(nevermindzzt@gmail.com)
* @brief shell passthrough mode
* @version 0.1
* @date 2021-05-31
*
* @copyright (c) 2021 Letter
*
*/
#ifndef __SHELL_PASSTHROUGH_H__
#define __SHELL_PASSTHROUGH_H__
#include "shell.h"
/**
* @brief 退出 passthrough 模式的按键键值,单字节
*/
#define SHELL_PASSTHROUGH_EXIT_KEY 0x04 // Ctrl + D
/**
* @brief passthrough 模式 handler 函数原型
*
* @param data 数据
* @param len 数据长度
*
* @return int 返回值
*/
typedef int (*ShellPassthrough)(char *data, unsigned short len);
/**
* @brief shell passthrough 模式函数名
*/
#define SHELL_PASSTROUGH_FUNC_NAME(_name) agency##_name
/**
* @brief shell passthrough 模式函数定义
*
* @param _name 命令名
* @param _prompt passthrough 模式提示符
* @param _handler passthrough 模式 handler
*/
#define SHELL_PASSTROUGH_FUNC(_name, _prompt, _handler) \
void SHELL_PASSTROUGH_FUNC_NAME(_name)(int p1, int p2) \
{ shellPassthrough(shellGetCurrent(), #_prompt, _handler, p1, (void *)p2); }
/**
* @brief shell passthrouh 定义
*
* @param _attr 属性
* @param _name passthrough 命令名
* @param _prompt passthrough 提示符
* @param _handler passthrough handler
* @param _desc passthrough 描述
*/
#define SHELL_EXPORT_PASSTROUGH(_attr, _name, _prompt, _handler, _desc) \
SHELL_PASSTROUGH_FUNC(_name, _prompt, _handler) \
SHELL_EXPORT_CMD(_attr|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), _name, SHELL_PASSTROUGH_FUNC_NAME(_name), _desc)
unsigned int shellPassthrough(Shell *shell, const char *prompt, ShellPassthrough handler, int argc, char *argv[]);
#endif
@@ -0,0 +1,24 @@
/**
* @file shell_secure_user.c
* @author Letter (nevermindzzt@gmail.com)
* @brief shell secure user
* @version 0.1
* @date 2022-03-12
*
* @copyright Copyright (c) 2022 Letter
*
*/
#include "shell_secure_user.h"
extern void shellSetUser(Shell *shell, const ShellCommand *user);
ShellCommand secureUser;
int shellSecureUser(Shell *shell, const char *name, int attr, ShellSecureUserGetPassword handler)
{
secureUser.attr.value = attr | SHELL_CMD_TYPE(SHELL_TYPE_USER);
secureUser.data.user.name = name;
secureUser.data.user.password = handler(name);
shellSetUser(shell, &secureUser);
return 0;
}
@@ -0,0 +1,58 @@
/**
* @file shell_secure_user.h
* @author Letter (nevermindzzt@gmail.com)
* @brief shell secure user
* @version 0.1
* @date 2022-03-12
*
* @copyright Copyright (c) 2022 Letter
*
*/
#ifndef __SHELL_SECURE_USER_H__
#define __SHELL_SECURE_USER_H__
#include "shell.h"
/**
* @brief shell secure user 获取用户密码函数原型
*
* @param name 用户名
*
* @return char* 用户密码
*/
typedef char* (*ShellSecureUserGetPassword)(const char *name);
/**
* @brief shell secure user 代理函数名
*
*/
#define SHELL_SECURE_USER_FUNC_NAME(_name) agency##_name
/**
* @brief shell secure user 代理函数定义
*
* @param _name 用户名
* @param _attr 用户命令属性
* @param _handler 获取用户密码函数
*/
#define SHELL_SECURE_USER_FUNC(_name, _attr, _handler) \
void SHELL_SECURE_USER_FUNC_NAME(_name)(int p1, int p2) \
{ shellSecureUser(shellGetCurrent(), #_name, _attr, _handler); }
/**
* @brief shell secure user 定义
*
* @param _attr 属性
* @param _name 用户名
* @param _handler 获取用户密码函数
* @param _desc 命令描述
*/
#define SHELL_EXPORT_SECURE_USER(_attr, _name, _handler, _desc) \
SHELL_SECURE_USER_FUNC(_name, _attr, _handler) \
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN, \
_name, SHELL_SECURE_USER_FUNC_NAME(_name), _desc)
int shellSecureUser(Shell *shell, const char *name, int attr, ShellSecureUserGetPassword handler);
#endif
@@ -0,0 +1,85 @@
# telnet
![version](https://img.shields.io/badge/version-1.0.0-brightgreen.svg)
![standard](https://img.shields.io/badge/standard-c99-brightgreen.svg)
![build](https://img.shields.io/badge/build-2021.08.07-brightgreen.svg)
![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)
基于letter shell运行的telnet
- [telnet](#telnet)
- [简介](#简介)
- [使用](#使用)
- [其他](#其他)
## 简介
telnet是基于letter shell运行的一个telnet服务端,可以在一些具有网络能力的嵌入式环境中使用,特别适用于现场不方便接线条件下的调试工作,telnet默认运行在23端口,可以在`telnetd.h`文件中进行配置,也可以在启动服务前,调用`telnetdSetPort`进行修改,注意23端口可能会被系统占用
## 使用
1. 修改包含的头文件
telnet使用标准的socket接口实现,运行此telnet实现需要你的运行环境具有socket接口,使用时,需要首先确认你的运行环境中,提供标准socket的头文件,然后替换`telnetd.c`文件中包含的头文件
```c
#include "sys/socket.h"
#include "arpa/inet.h"
#include "netinet/in.h"
```
2. 实现线程接口
telnet的运行需要多任务支持,你选哟提供一个用于创建新线程的接口,下面是在linux环境下的示例
```c
/**
* @brief 新线程接口
*
* @param handler 线程函数
* @param param 线程参数
*
* @return int 0 启动成功 -1 启动失败
*/
int userNewThread(void *handler, void *param)
{
pthread_t tid;
return pthread_create(&tid, NULL, handler, param) == 0 ? 0 : -1;
}
```
3. 初始化
```c
telnetdInit(userNewThread);
```
4. 启动
直接调用`telnetdStart`函数,或者在shell执行`telnetd start`命令,启动telent server
## 其他
- 只支持一个客户端连接
做telnet这个功能的时候发现,letter shell目前提供的接口,不好在telnet上做多客户端支持,遂暂时只支持一个客户端连接
- 部分shell功能不可用
目前的telent实现没有实现letter shell的`read`接口,所以部分依赖于此接口的功能,比如说`shellScan`都是不可用的
- 使用telent需要开启letter shell的伴生对象功能
- 性能优化
对于资源紧张的单片机,telnet频繁的网络操作可能会对系统性能产生压力,此时建议优化`telentd.c`中的`telnetdWrite`函数,可以使用环形缓冲区,每次此接口写入的数据不直接调用`send`函数,而是写入缓冲区,然后定时(100ms左右)调用`send`函数清空缓冲区
- x86 demo
x86 demo中实现了telnet的接口,可以直接运行试用
```sh
letter:/$ telnetd setPort 4545
letter:/$ telnetd start
```
@@ -0,0 +1,208 @@
/**
* @file telnetd.c
* @author Letter
* @brief telnet server for letter shell
* @version 0.1
* @date 2021-08-07
*
* @copyright (c) 2021 Letter
*
*/
#include "telnetd.h"
#include "sys/socket.h"
#include "arpa/inet.h"
#include "netinet/in.h"
#include "shell.h"
#include "shell_cmd_group.h"
#if SHELL_USING_COMPANION != 1
#error telent for letter shell can not be used while shell companion is diabled
#endif
/**
* @brief 新线程接口实例
*/
static NewThread newThread;
/**
* @brief telnet server socket
*/
static int telnetdSocket;
/**
* @brief telent server shell
*/
static Shell *telnetdShell;
/**
* @brief telnet server 监听端口
*/
static int telnetdPort = TELNETD_DEFAULT_SERVER_PORT;
static void telnetdServer(void);
static void telnetdConnection(int client);
static void telentdWrite(char *data, short len);
/**
* @brief telnet 协议命令
*/
static char telnetCmd[] = {0xff, 0xfb, 0x01, 0xff, 0xfb, 0x03, 0xff, 0xfc, 0x1f};
/**
* @brief telnet server初始化
*
* @param newThreadInterface 新线程接口
*
* @return int 0 启动telent成功 -1 启动失败
*/
int telentdInit(NewThread newThreadInterface)
{
newThread = newThreadInterface;
return newThread ? 0 : -1;
}
/**
* @brief 启动 telnet server
*
* @return int 0 启动telent成功 -1 启动失败
*/
int telnetdStart()
{
return newThread ? newThread(telnetdServer, NULL) : -1;
}
/**
* @brief 停止 telnet server
*
*/
void telnetdStop()
{
close(telnetdSocket);
if (telnetdShell != NULL)
{
close((int) shellCompanionGet(telnetdShell, SHELL_COMPANION_ID_TELNETD));
}
}
/**
* @brief 修改telnet server 监听端口
*
* @param port 端口
*
*/
void telnetdSetPort(int port)
{
telnetdPort = port;
}
/**
* @brief telent 服务
*
*
*/
static void telnetdServer(void)
{
struct sockaddr_in telnetdAddr;
int client;
struct sockaddr_in clientAddr;
int addrLen = sizeof(clientAddr);
telnetdSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
memset(&telnetdAddr, 0, sizeof(telnetdAddr));
telnetdAddr.sin_family = AF_INET,
telnetdAddr.sin_addr.s_addr = inet_addr(TELNETD_SERVER_ADDRESS);
telnetdAddr.sin_port = htons(telnetdPort);
if (bind(telnetdSocket, (struct sockaddr *)&telnetdAddr, sizeof(telnetdAddr)) == 0 &&
listen(telnetdSocket, 5) == 0)
{
while (1)
{
client = accept(telnetdSocket, (struct sockaddr *) &clientAddr, (socklen_t *) &addrLen);
if (client > 0)
{
newThread(telnetdConnection, (void *) client);
}
else if (client <= 0)
{
break;
}
}
}
close(telnetdSocket);
}
/**
* @brief telnet server 连接处理
*
* @param client 客户端连接socket
*
*/
static void telnetdConnection(int client)
{
int len = 0;
char *data = SHELL_MALLOC(16);
char *shellBuffer = SHELL_MALLOC(TELNETD_SHELL_BUFFER_SIZE);
telnetdShell = SHELL_MALLOC(sizeof(Shell));
/** 处理 telent 协议 */
send(client, telnetCmd, 9, 0);
recv(client, data, 6, 0);
telnetdShell->write = telentdWrite;
shellCompanionAdd(telnetdShell, SHELL_COMPANION_ID_TELNETD, (void *)client);
shellInit(telnetdShell, shellBuffer, TELNETD_SHELL_BUFFER_SIZE);
if (TELNETD_SHELL_USER)
{
shellRun(telnetdShell, TELNETD_SHELL_USER);
}
while (1)
{
len = recv(client, data, 1, 0);
if (len == 0)
{
break;
}
else if (len == 1)
{
shellHandler(telnetdShell, data[0]);
}
}
shellDeInit(telnetdShell);
SHELL_FREE(data);
SHELL_FREE(shellBuffer);
SHELL_FREE(telnetdShell);
telnetdShell = NULL;
close(client);
}
/**
* @brief telnet server数据写
*
* @param data 写入的数据
* @param len 数据长度
*
*/
static void telentdWrite(char *data, short len)
{
int client = (int) shellCompanionGet(telnetdShell, SHELL_COMPANION_ID_TELNETD);
if (client != 0)
{
send(client, data, len, 0);
}
}
ShellCommand telnetdGroup[] =
{
SHELL_CMD_GROUP_ITEM(SHELL_TYPE_CMD_FUNC, start, telnetdStart, start telnet server),
SHELL_CMD_GROUP_ITEM(SHELL_TYPE_CMD_FUNC, stop, telnetdStop, stop telnet server),
SHELL_CMD_GROUP_ITEM(SHELL_TYPE_CMD_FUNC, setPort, telnetdSetPort, set telnet server port),
SHELL_CMD_GROUP_END()
};
SHELL_EXPORT_CMD_GROUP(
SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN,
telnetd, telnetdGroup, telnet server\ninput telent -h for more help);
@@ -0,0 +1,76 @@
/**
* @file telnetd.h
* @author Letter
* @brief telnet server for letter shell
* @version 0.1
* @date 2021-08-07
*
* @copyright (c) 2021 Letter
*
*/
#ifndef __TELNETD_H__
#define __TELNETD_H__
/**
* @brief 版本
*/
#define TELNET_VERSION "1.0.0"
/**
* @brief telnetd shell伴生对象ID
*/
#define SHELL_COMPANION_ID_TELNETD -3
/**
* @brief telnet server 监听的IP
*/
#define TELNETD_SERVER_ADDRESS "127.0.0.1"
/**
* @brief telnet 默认监听端口
*/
#define TELNETD_DEFAULT_SERVER_PORT 23
/**
* @brief telnet shell的数据缓冲区大小
*/
#define TELNETD_SHELL_BUFFER_SIZE 512
/**
* @brief telnet shell的用户名,使用默认shell用户设置为NULL即可
*/
#define TELNETD_SHELL_USER NULL
/**
* @brief 新线程接口
*
* @param void * 线程执行函数
* @param void * 线程参数
*
* @reutrn int 0 启动线程成功 -1 启动线程失败
*/
typedef int (*NewThread)(void *, void *);
/**
* @brief telnet server初始化
*
* @param newThreadInterface 新线程接口
*
* @return int 0 启动telent server成功 -1 启动失败
*/
int telentdInit(NewThread newThreadInterface);
/**
* @brief 启动 telnet server
*
* @return int 0 启动telent成功 -1 启动失败
*/
int telnetdStart();
/**
* @brief 停止 telnet server
*
*/
void telnetdStop();
#endif
File diff suppressed because it is too large. Load diff
+472
View File
@@ -0,0 +1,472 @@
/**
* @file shell.h
* @author Letter (NevermindZZT@gmail.com)
* @brief letter shell
* @version 3.0.0
* @date 2019-12-30
*
* @copyright (c) 2020 Letter
*
*/
#ifndef __SHELL_H__
#define __SHELL_H__
#include "shell_cfg.h"
#define SHELL_VERSION "3.1.2" /**< 版本号 */
/**
* @brief shell 断言
*
* @param expr 表达式
* @param action 断言失败操作
*/
#define SHELL_ASSERT(expr, action) \
if (!(expr)) { \
action; \
}
#if SHELL_USING_LOCK == 1
#define SHELL_LOCK(shell) shell->lock(shell)
#define SHELL_UNLOCK(shell) shell->unlock(shell)
#else
#define SHELL_LOCK(shell)
#define SHELL_UNLOCK(shell)
#endif /** SHELL_USING_LOCK == 1 */
/**
* @brief shell 命令权限
*
* @param permission 权限级别
*/
#define SHELL_CMD_PERMISSION(permission) \
(permission & 0x000000FF)
/**
* @brief shell 命令类型
*
* @param type 类型
*/
#define SHELL_CMD_TYPE(type) \
((type & 0x0000000F) << 8)
/**
* @brief 使能命令在未校验密码的情况下使用
*/
#define SHELL_CMD_ENABLE_UNCHECKED \
(1 << 12)
/**
* @brief 禁用返回值打印
*/
#define SHELL_CMD_DISABLE_RETURN \
(1 << 13)
/**
* @brief 只读属性(仅对变量生效)
*/
#define SHELL_CMD_READ_ONLY \
(1 << 14)
/**
* @brief 命令参数数量
*/
#define SHELL_CMD_PARAM_NUM(num) \
((num & 0x0000000F)) << 16
#ifndef SHELL_SECTION
#if defined(__CC_ARM) || defined(__CLANG_ARM)
#define SHELL_SECTION(x) __attribute__((section(x)))
#elif defined (__IAR_SYSTEMS_ICC__)
#define SHELL_SECTION(x) @ x
#elif defined(__GNUC__)
#define SHELL_SECTION(x) __attribute__((section(x)))
#else
#define SHELL_SECTION(x)
#endif
#endif
#ifndef SHELL_USED
#if defined(__CC_ARM) || defined(__CLANG_ARM)
#define SHELL_USED __attribute__((used))
#elif defined (__IAR_SYSTEMS_ICC__)
#define SHELL_USED __root
#elif defined(__GNUC__)
#define SHELL_USED __attribute__((used))
#else
#define SHELL_USED
#endif
#endif
/**
* @brief shell float型参数转换
*/
#define SHELL_PARAM_FLOAT(x) (*(float *)(&x))
/**
* @brief shell 代理函数名
*/
#define SHELL_AGENCY_FUNC_NAME(_func) agency##_func
/**
* @brief shell代理函数定义
*
* @param _func 被代理的函数
* @param ... 代理参数
*/
#define SHELL_AGENCY_FUNC(_func, ...) \
void SHELL_AGENCY_FUNC_NAME(_func)(int p1, int p2, int p3, int p4, int p5, int p6, int p7) \
{ _func(__VA_ARGS__); }
#if SHELL_USING_CMD_EXPORT == 1
/**
* @brief shell 命令定义
*
* @param _attr 命令属性
* @param _name 命令名
* @param _func 命令函数
* @param _desc 命令描述
*/
#define SHELL_EXPORT_CMD(_attr, _name, _func, _desc) \
const char shellCmd##_name[] = #_name; \
const char shellDesc##_name[] = #_desc; \
SHELL_USED const ShellCommand \
shellCommand##_name SHELL_SECTION("shellCommand") = \
{ \
.attr.value = _attr, \
.data.cmd.name = shellCmd##_name, \
.data.cmd.function = (int (*)())_func, \
.data.cmd.desc = shellDesc##_name \
}
/**
* @brief shell 代理命令定义
*
* @param _attr 命令属性
* @param _name 命令名
* @param _func 命令函数
* @param _desc 命令描述
* @param ... 代理参数
*/
#define SHELL_EXPORT_CMD_AGENCY(_attr, _name, _func, _desc, ...) \
SHELL_AGENCY_FUNC(_func, ##__VA_ARGS__) \
SHELL_EXPORT_CMD(_attr, _name, SHELL_AGENCY_FUNC_NAME(_func), _desc)
/**
* @brief shell 变量定义
*
* @param _attr 变量属性
* @param _name 变量名
* @param _value 变量值
* @param _desc 变量描述
*/
#define SHELL_EXPORT_VAR(_attr, _name, _value, _desc) \
const char shellCmd##_name[] = #_name; \
const char shellDesc##_name[] = #_desc; \
SHELL_USED const ShellCommand \
shellVar##_name SHELL_SECTION("shellCommand") = \
{ \
.attr.value = _attr, \
.data.var.name = shellCmd##_name, \
.data.var.value = (void *)_value, \
.data.var.desc = shellDesc##_name \
}
/**
* @brief shell 用户定义
*
* @param _attr 用户属性
* @param _name 用户名
* @param _password 用户密码
* @param _desc 用户描述
*/
#define SHELL_EXPORT_USER(_attr, _name, _password, _desc) \
const char shellCmd##_name[] = #_name; \
const char shellPassword##_name[] = #_password; \
const char shellDesc##_name[] = #_desc; \
SHELL_USED const ShellCommand \
shellUser##_name SHELL_SECTION("shellCommand") = \
{ \
.attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_USER), \
.data.user.name = shellCmd##_name, \
.data.user.password = shellPassword##_name, \
.data.user.desc = shellDesc##_name \
}
/**
* @brief shell 按键定义
*
* @param _attr 按键属性
* @param _value 按键键值
* @param _func 按键函数
* @param _desc 按键描述
*/
#define SHELL_EXPORT_KEY(_attr, _value, _func, _desc) \
const char shellDesc##_value[] = #_desc; \
SHELL_USED const ShellCommand \
shellKey##_value SHELL_SECTION("shellCommand") = \
{ \
.attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_KEY), \
.data.key.value = _value, \
.data.key.function = (void (*)(Shell *))_func, \
.data.key.desc = shellDesc##_value \
}
/**
* @brief shell 代理按键定义
*
* @param _attr 按键属性
* @param _value 按键键值
* @param _func 按键函数
* @param _desc 按键描述
* @param ... 代理参数
*/
#define SHELL_EXPORT_KEY_AGENCY(_attr, _value, _func, _desc, ...) \
SHELL_AGENCY_FUNC(_func, ##__VA_ARGS__) \
SHELL_EXPORT_KEY(_attr, _value, SHELL_AGENCY_FUNC_NAME(_func), _desc)
#else
/**
* @brief shell 命令item定义
*
* @param _attr 命令属性
* @param _name 命令名
* @param _func 命令函数
* @param _desc 命令描述
*/
#define SHELL_CMD_ITEM(_attr, _name, _func, _desc) \
{ \
.attr.value = _attr, \
.data.cmd.name = #_name, \
.data.cmd.function = (int (*)())_func, \
.data.cmd.desc = #_desc \
}
/**
* @brief shell 变量item定义
*
* @param _attr 变量属性
* @param _name 变量名
* @param _value 变量值
* @param _desc 变量描述
*/
#define SHELL_VAR_ITEM(_attr, _name, _value, _desc) \
{ \
.attr.value = _attr, \
.data.var.name = #_name, \
.data.var.value = (void *)_value, \
.data.var.desc = #_desc \
}
/**
* @brief shell 用户item定义
*
* @param _attr 用户属性
* @param _name 用户名
* @param _password 用户密码
* @param _desc 用户描述
*/
#define SHELL_USER_ITEM(_attr, _name, _password, _desc) \
{ \
.attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_USER), \
.data.user.name = #_name, \
.data.user.password = #_password, \
.data.user.desc = #_desc \
}
/**
* @brief shell 按键item定义
*
* @param _attr 按键属性
* @param _value 按键键值
* @param _func 按键函数
* @param _desc 按键描述
*/
#define SHELL_KEY_ITEM(_attr, _value, _func, _desc) \
{ \
.attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_KEY), \
.data.key.value = _value, \
.data.key.function = (void (*)(Shell *))_func, \
.data.key.desc = #_desc \
}
#define SHELL_EXPORT_CMD(_attr, _name, _func, _desc)
#define SHELL_EXPORT_CMD_AGENCY(_attr, _name, _func, _desc, ...)
#define SHELL_EXPORT_VAR(_attr, _name, _value, _desc)
#define SHELL_EXPORT_USER(_attr, _name, _password, _desc)
#define SHELL_EXPORT_KEY(_attr, _value, _func, _desc)
#define SHELL_EXPORT_KEY_AGENCY(_attr, _name, _func, _desc, ...)
#endif /** SHELL_USING_CMD_EXPORT == 1 */
/**
* @brief shell command类型
*/
typedef enum
{
SHELL_TYPE_CMD_MAIN = 0, /**< main形式命令 */
SHELL_TYPE_CMD_FUNC, /**< C函数形式命令 */
SHELL_TYPE_VAR_INT, /**< int型变量 */
SHELL_TYPE_VAR_SHORT, /**< short型变量 */
SHELL_TYPE_VAR_CHAR, /**< char型变量 */
SHELL_TYPE_VAR_STRING, /**< string型变量 */
SHELL_TYPE_VAR_POINT, /**< 指针型变量 */
SHELL_TYPE_VAR_NODE, /**< 节点变量 */
SHELL_TYPE_USER, /**< 用户 */
SHELL_TYPE_KEY, /**< 按键 */
} ShellCommandType;
/**
* @brief Shell定义
*/
typedef struct shell_def
{
struct
{
const struct shell_command *user; /**< 当前用户 */
int activeTime; /**< shell激活时间 */
char *path; /**< 当前shell路径 */
#if SHELL_USING_COMPANION == 1
struct shell_companion_object *companions; /**< 伴生对象 */
#endif
#if SHELL_KEEP_RETURN_VALUE == 1
int retVal; /**< 返回值 */
#endif
} info;
struct
{
unsigned short length; /**< 输入数据长度 */
unsigned short cursor; /**< 当前光标位置 */
char *buffer; /**< 输入缓冲 */
char *param[SHELL_PARAMETER_MAX_NUMBER]; /**< 参数 */
unsigned short bufferSize; /**< 输入缓冲大小 */
unsigned short paramCount; /**< 参数数量 */
int keyValue; /**< 输入按键键值 */
} parser;
#if SHELL_HISTORY_MAX_NUMBER > 0
struct
{
char *item[SHELL_HISTORY_MAX_NUMBER]; /**< 历史记录 */
unsigned short number; /**< 历史记录数 */
unsigned short record; /**< 当前记录位置 */
signed short offset; /**< 当前历史记录偏移 */
} history;
#endif /** SHELL_HISTORY_MAX_NUMBER > 0 */
struct
{
void *base; /**< 命令表基址 */
unsigned short count; /**< 命令数量 */
} commandList;
struct
{
unsigned char isChecked : 1; /**< 密码校验通过 */
unsigned char isActive : 1; /**< 当前活动Shell */
unsigned char tabFlag : 1; /**< tab标志 */
} status;
signed short (*read)(char *, unsigned short); /**< shell读函数 */
signed short (*write)(char *, unsigned short); /**< shell写函数 */
#if SHELL_USING_LOCK == 1
int (*lock)(struct shell_def *); /**< shell 加锁 */
int (*unlock)(struct shell_def *); /**< shell 解锁 */
#endif
} Shell;
/**
* @brief shell command定义
*/
typedef struct shell_command
{
union
{
struct
{
unsigned char permission : 8; /**< command权限 */
ShellCommandType type : 4; /**< command类型 */
unsigned char enableUnchecked : 1; /**< 在未校验密码的情况下可用 */
unsigned char disableReturn : 1; /**< 禁用返回值输出 */
unsigned char readOnly : 1; /**< 只读 */
unsigned char reserve : 1; /**< 保留 */
unsigned char paramNum : 4; /**< 参数数量 */
} attrs;
int value;
} attr; /**< 属性 */
union
{
struct
{
const char *name; /**< 命令名 */
int (*function)(); /**< 命令执行函数 */
const char *desc; /**< 命令描述 */
} cmd; /**< 命令定义 */
struct
{
const char *name; /**< 变量名 */
void *value; /**< 变量值 */
const char *desc; /**< 变量描述 */
} var; /**< 变量定义 */
struct
{
const char *name; /**< 用户名 */
const char *password; /**< 用户密码 */
const char *desc; /**< 用户描述 */
} user; /**< 用户定义 */
struct
{
int value; /**< 按键键值 */
void (*function)(Shell *); /**< 按键执行函数 */
const char *desc; /**< 按键描述 */
} key; /**< 按键定义 */
} data;
} ShellCommand;
/**
* @brief shell节点变量属性
*/
typedef struct
{
void *var; /**< 变量引用 */
int (*get)(); /**< 变量get方法 */
int (*set)(); /**< 变量set方法 */
} ShellNodeVarAttr;
#define shellSetPath(_shell, _path) (_shell)->info.path = _path
#define shellGetPath(_shell) ((_shell)->info.path)
#define shellDeInit(shell) shellRemove(shell)
void shellInit(Shell *shell, char *buffer, unsigned short size);
void shellRemove(Shell *shell);
unsigned short shellWriteString(Shell *shell, const char *string);
void shellPrint(Shell *shell, const char *fmt, ...);
void shellScan(Shell *shell, char *fmt, ...);
Shell* shellGetCurrent(void);
void shellHandler(Shell *shell, char data);
void shellWriteEndLine(Shell *shell, char *buffer, int len);
void shellTask(void *param);
int shellRun(Shell *shell, const char *cmd);
#if SHELL_USING_COMPANION == 1
/**
* @brief shell伴生对象定义
*/
typedef struct shell_companion_object
{
int id; /**< 伴生对象ID */
void *obj; /**< 伴生对象 */
struct shell_companion_object *next; /**< 下一个伴生对象 */
} ShellCompanionObj;
signed char shellCompanionAdd(Shell *shell, int id, void *object);
signed char shellCompanionDel(Shell *shell, int id);
void *shellCompanionGet(Shell *shell, int id);
#endif
#endif
+188
View File
@@ -0,0 +1,188 @@
/**
* @file shell_cfg.h
* @author Letter (nevermindzzt@gmail.com)
* @brief shell config
* @version 3.0.0
* @date 2019-12-31
*
* @copyright (c) 2019 Letter
*
*/
#ifndef __SHELL_CFG_H__
#define __SHELL_CFG_H__
/**
* @brief 是否使用默认shell任务while循环,使能宏`SHELL_USING_TASK`后此宏有意义
* 使能此宏,则`shellTask()`函数会一直循环读取输入,一般使用操作系统建立shell
* 任务时使能此宏,关闭此宏的情况下,一般适用于无操作系统,在主循环中调用`shellTask()`
*/
#define SHELL_TASK_WHILE 1
/**
* @brief 是否使用命令导出方式
* 使能此宏后,可以使用`SHELL_EXPORT_CMD()`等导出命令
* 定义shell命令,关闭此宏的情况下,需要使用命令表的方式
*/
#define SHELL_USING_CMD_EXPORT 1
/**
* @brief 是否使用shell伴生对象
* 一些扩展的组件(文件系统支持,日志工具等)需要使用伴生对象
*/
#define SHELL_USING_COMPANION 0
/**
* @brief 支持shell尾行模式
*/
#define SHELL_SUPPORT_END_LINE 0
/**
* @brief 是否在输出命令列表中列出用户
*/
#define SHELL_HELP_LIST_USER 0
/**
* @brief 是否在输出命令列表中列出变量
*/
#define SHELL_HELP_LIST_VAR 0
/**
* @brief 是否在输出命令列表中列出按键
*/
#define SHELL_HELP_LIST_KEY 0
/**
* @brief 是否在输出命令列表中展示命令权限
*/
#define SHELL_HELP_SHOW_PERMISSION 1
/**
* @brief 使用LF作为命令行回车触发
* 可以和SHELL_ENTER_CR同时开启
*/
#define SHELL_ENTER_LF 1
/**
* @brief 使用CR作为命令行回车触发
* 可以和SHELL_ENTER_LF同时开启
*/
#define SHELL_ENTER_CR 1
/**
* @brief 使用CRLF作为命令行回车触发
* 不可以和SHELL_ENTER_LF或SHELL_ENTER_CR同时开启
*/
#define SHELL_ENTER_CRLF 0
/**
* @brief 使用执行未导出函数的功能
* 启用后,可以通过`exec [addr] [args]`直接执行对应地址的函数
* @attention 如果地址错误,可能会直接引起程序崩溃
*/
#define SHELL_EXEC_UNDEF_FUNC 0
/**
* @brief shell命令参数最大数量
* 包含命令名在内,超过16个参数并且使用了参数自动转换的情况下,需要修改源码
*/
#define SHELL_PARAMETER_MAX_NUMBER 8
/**
* @brief 历史命令记录数量
*/
#define SHELL_HISTORY_MAX_NUMBER 5
/**
* @brief 双击间隔(ms)
* 使能宏`SHELL_LONG_HELP`后此宏生效,定义双击tab补全help的时间间隔
*/
#define SHELL_DOUBLE_CLICK_TIME 200
/**
* @brief 快速帮助
* 作用于双击tab的场景,当使能此宏时,双击tab不会对命令进行help补全,而是直接显示对应命令的帮助信息
*/
#define SHELL_QUICK_HELP 1
/**
* @brief 保存命令返回值
* 开启后会默认定义一个`RETVAL`变量,会保存上一次命令执行的返回值,可以在随后的命令中进行调用
* 如果命令的`SHELL_CMD_DISABLE_RETURN`标志被设置,则该命令不会更新`RETVAL`
*/
#define SHELL_KEEP_RETURN_VALUE 0
/**
* @brief 管理的最大shell数量
*/
#define SHELL_MAX_NUMBER 5
/**
* @brief shell格式化输出的缓冲大小
* 为0时不使用shell格式化输出
*/
#define SHELL_PRINT_BUFFER 128
/**
* @brief shell格式化输入的缓冲大小
* 为0时不使用shell格式化输入
* @note shell格式化输入会阻塞shellTask, 仅适用于在有操作系统的情况下使用
*/
#define SHELL_SCAN_BUFFER 0
/**
* @brief 获取系统时间(ms)
* 定义此宏为获取系统Tick,如`HAL_GetTick()`
* @note 此宏不定义时无法使用双击tab补全命令help,无法使用shell超时锁定
*/
#define SHELL_GET_TICK() 0
/**
* @brief 使用锁
* @note 使用shell锁时,需要对加锁和解锁进行实现
*/
#define SHELL_USING_LOCK 0
/**
* @brief shell内存分配
* shell本身不需要此接口,若使用shell伴生对象,需要进行定义
*/
#define SHELL_MALLOC(size) 0
/**
* @brief shell内存释放
* shell本身不需要此接口,若使用shell伴生对象,需要进行定义
*/
#define SHELL_FREE(obj) 0
/**
* @brief 是否显示shell信息
*/
#define SHELL_SHOW_INFO 1
/**
* @brief 是否在登录后清除命令行
*/
#define SHELL_CLS_WHEN_LOGIN 1
/**
* @brief shell默认用户
*/
#define SHELL_DEFAULT_USER "letter"
/**
* @brief shell默认用户密码
* 若默认用户不需要密码,设为""
*/
#define SHELL_DEFAULT_USER_PASSWORD ""
/**
* @brief shell自动锁定超时
* shell当前用户密码有效的时候生效,超时后会自动重新锁定shell
* 设置为0时关闭自动锁定功能,时间单位为`SHELL_GET_TICK()`单位
* @note 使用超时锁定必须保证`SHELL_GET_TICK()`有效
*/
#define SHELL_LOCK_TIMEOUT 0 * 60 * 1000
#endif
@@ -0,0 +1,103 @@
/**
* @file shell_cmd_list.c
* @author Letter (NevermindZZT@gmail.com)
* @brief shell cmd list
* @version 3.0.0
* @date 2020-01-17
*
* @copyright (c) 2020 Letter
*
*/
#include "shell.h"
#if SHELL_USING_CMD_EXPORT != 1
extern int shellSetVar(char *name, int value);
extern void shellUp(Shell *shell);
extern void shellDown(Shell *shell);
extern void shellRight(Shell *shell);
extern void shellLeft(Shell *shell);
extern void shellTab(Shell *shell);
extern void shellBackspace(Shell *shell);
extern void shellDelete(Shell *shell);
extern void shellEnter(Shell *shell);
extern void shellHelp(int argc, char *argv[]);
extern void shellUsers(void);
extern void shellCmds(void);
extern void shellVars(void);
extern void shellKeys(void);
extern void shellClear(void);
#if SHELL_EXEC_UNDEF_FUNC == 1
extern int shellExecute(int argc, char *argv[]);
#endif
SHELL_AGENCY_FUNC(shellRun, shellGetCurrent(), (const char *)p1);
/**
* @brief shell命令表
*
*/
const ShellCommand shellCommandList[] =
{
{.attr.value=SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_USER),
.data.user.name = SHELL_DEFAULT_USER,
.data.user.password = SHELL_DEFAULT_USER_PASSWORD,
.data.user.desc = "default user"},
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
setVar, shellSetVar, set var),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0), 0x1B5B4100, shellUp, up),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0), 0x1B5B4200, shellDown, down),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x1B5B4300, shellRight, right),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x1B5B4400, shellLeft, left),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0), 0x09000000, shellTab, tab),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x08000000, shellBackspace, backspace),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x7F000000, shellDelete, delete),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x1B5B337E, shellDelete, delete),
#if SHELL_ENTER_LF == 1
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x0A000000, shellEnter, enter),
#endif
#if SHELL_ENTER_CR == 1
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x0D000000, shellEnter, enter),
#endif
#if SHELL_ENTER_CRLF == 1
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x0D0A0000, shellEnter, enter),
#endif
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN,
help, shellHelp, show command info\r\nhelp [cmd]),
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
users, shellUsers, list all user),
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
cmds, shellCmds, list all cmd),
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
vars, shellVars, list all var),
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
keys, shellKeys, list all key),
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
clear, shellClear, clear console),
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
sh, SHELL_AGENCY_FUNC_NAME(shellRun), run command directly),
#if SHELL_EXEC_UNDEF_FUNC == 1
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN,
exec, shellExecute, execute function undefined),
#endif
};
/**
* @brief shell命令表大小
*
*/
const unsigned short shellCommandCount
= sizeof(shellCommandList) / sizeof(ShellCommand);
#endif
@@ -0,0 +1,87 @@
/**
* @file shell_companion.c
* @author Letter (nevermindzzt@gmail.com)
* @brief shell companion object support
* @version 3.0.3
* @date 2020-07-22
*
* @copyright (c) 2020 Letter
*
*/
#include "shell.h"
#if SHELL_USING_COMPANION == 1
/**
* @brief shell添加伴生对象
*
* @param shell shell对象
* @param id 伴生对象ID
* @param object 伴生对象
* @return signed char 0 添加成功 -1 添加失败
*/
signed char shellCompanionAdd(Shell *shell, int id, void *object)
{
ShellCompanionObj *companions = shell->info.companions;
ShellCompanionObj *node = SHELL_MALLOC(sizeof(ShellCompanionObj));
SHELL_ASSERT(node, return -1);
node->id = id;
node->obj = object;
node->next = companions;
shell->info.companions = node;
return 0;
}
/**
* @brief shell删除伴生对象
*
* @param shell shell对象
* @param id 伴生对象ID
* @return signed char 0 删除成功 -1 无匹配对象
*/
signed char shellCompanionDel(Shell *shell, int id)
{
ShellCompanionObj *companions = shell->info.companions;
ShellCompanionObj *front = companions;
while (companions)
{
if (companions->id == id)
{
if (companions == shell->info.companions && !(companions->next))
{
shell->info.companions = (void *)0;
}
else
{
front->next = companions->next;
}
SHELL_FREE(companions);
return 0;
}
front = companions;
companions = companions->next;
}
return -1;
}
/**
* @brief shell获取伴生对象
*
* @param shell shell对象
* @param id 伴生对象ID
* @return void* 伴生对象,无匹配对象时返回NULL
*/
void *shellCompanionGet(Shell *shell, int id)
{
SHELL_ASSERT(shell, return (void *)0);
ShellCompanionObj *companions = shell->info.companions;
while (companions)
{
if (companions->id == id)
{
return companions->obj;
}
companions = companions->next;
}
return (void *)0;
}
#endif /** SHELL_USING_COMPANION == 1 */
+447
View File
@@ -0,0 +1,447 @@
/**
* @file shell_ext.c
* @author Letter (NevermindZZT@gmail.com)
* @brief shell extensions
* @version 3.0.0
* @date 2019-12-31
*
* @copyright (c) 2019 Letter
*
*/
#include "shell_cfg.h"
#include "shell.h"
#include "shell_ext.h"
extern ShellCommand* shellSeekCommand(Shell *shell,
const char *cmd,
ShellCommand *base,
unsigned short compareLength);
extern int shellGetVarValue(Shell *shell, ShellCommand *command);
/**
* @brief 判断数字进制
*
* @param string 参数字符串
* @return ShellNumType 进制
*/
static ShellNumType shellExtNumType(char *string)
{
char *p = string;
ShellNumType type = NUM_TYPE_DEC;
if ((*p == '0') && ((*(p + 1) == 'x') || (*(p + 1) == 'X')))
{
type = NUM_TYPE_HEX;
}
else if ((*p == '0') && ((*(p + 1) == 'b') || (*(p + 1) == 'B')))
{
type = NUM_TYPE_BIN;
}
else if (*p == '0')
{
type = NUM_TYPE_OCT;
}
while (*p++)
{
if (*p == '.' && *(p + 1) != 0)
{
type = NUM_TYPE_FLOAT;
break;
}
}
return type;
}
/**
* @brief 字符转数字
*
* @param code 字符
* @return char 数字
*/
static char shellExtToNum(char code)
{
if ((code >= '0') && (code <= '9'))
{
return code -'0';
}
else if ((code >= 'a') && (code <= 'f'))
{
return code - 'a' + 10;
}
else if ((code >= 'A') && (code <= 'F'))
{
return code - 'A' + 10;
}
else
{
return 0;
}
}
/**
* @brief 解析字符参数
*
* @param string 字符串参数
* @return char 解析出的字符
*/
static char shellExtParseChar(char *string)
{
char *p = string + 1;
char value = 0;
if (*p == '\\')
{
switch (*(p + 1))
{
case 'b':
value = '\b';
break;
case 'r':
value = '\r';
break;
case 'n':
value = '\n';
break;
case 't':
value = '\t';
break;
case '0':
value = 0;
break;
default:
value = *(p + 1);
break;
}
}
else
{
value = *p;
}
return value;
}
/**
* @brief 解析字符串参数
*
* @param string 字符串参数
* @return char* 解析出的字符串
*/
static char* shellExtParseString(char *string)
{
char *p = string;
unsigned short index = 0;
if (*string == '\"')
{
p = ++string;
}
while (*p)
{
if (*p == '\\')
{
*(string + index) = shellExtParseChar(p - 1);
p++;
}
else if (*p == '\"')
{
*(string + index) = 0;
}
else
{
*(string + index) = *p;
}
p++;
index ++;
}
*(string + index) = 0;
return string;
}
/**
* @brief 解析数字参数
*
* @param string 字符串参数
* @return unsigned int 解析出的数字
*/
static unsigned int shellExtParseNumber(char *string)
{
ShellNumType type = NUM_TYPE_DEC;
char radix = 10;
char *p = string;
char offset = 0;
signed char sign = 1;
unsigned int valueInt = 0;
float valueFloat = 0.0;
unsigned int devide = 0;
if (*string == '-')
{
sign = -1;
}
type = shellExtNumType(string + ((sign == -1) ? 1 : 0));
switch ((char)type)
{
case NUM_TYPE_HEX:
radix = 16;
offset = 2;
break;
case NUM_TYPE_OCT:
radix = 8;
offset = 1;
break;
case NUM_TYPE_BIN:
radix = 2;
offset = 2;
break;
default:
break;
}
p = string + offset + ((sign == -1) ? 1 : 0);
while (*p)
{
if (*p == '.')
{
devide = 1;
p++;
continue;
}
valueInt = valueInt * radix + shellExtToNum(*p);
devide *= 10;
p++;
}
if (type == NUM_TYPE_FLOAT && devide != 0)
{
valueFloat = (float)valueInt / devide * sign;
return *(unsigned int *)(&valueFloat);
}
else
{
return valueInt * sign;
}
}
/**
* @brief 解析变量参数
*
* @param shell shell对象
* @param var 变量
* @return unsigned int 变量值
*/
static unsigned int shellExtParseVar(Shell *shell, char *var)
{
ShellCommand *command = shellSeekCommand(shell,
var + 1,
shell->commandList.base,
0);
if (command)
{
return shellGetVarValue(shell, command);
}
else
{
return 0;
}
}
/**
* @brief 解析参数
*
* @param shell shell对象
* @param string 参数
* @return unsigned int 解析结果
*/
unsigned int shellExtParsePara(Shell *shell, char *string)
{
if (*string == '\'' && *(string + 1))
{
return (unsigned int)shellExtParseChar(string);
}
else if (*string == '-' || (*string >= '0' && *string <= '9'))
{
return (unsigned int)shellExtParseNumber(string);
}
else if (*string == '$' && *(string + 1))
{
return shellExtParseVar(shell, string);
}
else if (*string)
{
return (unsigned int)shellExtParseString(string);
}
return 0;
}
/**
* @brief 执行命令
*
* @param shell shell对象
* @param command 命令
* @param argc 参数个数
* @param argv 参数
* @return int 返回值
*/
int shellExtRun(Shell *shell, ShellCommand *command, int argc, char *argv[])
{
unsigned int params[SHELL_PARAMETER_MAX_NUMBER] = {0};
int paramNum = command->attr.attrs.paramNum > (argc - 1) ?
command->attr.attrs.paramNum : (argc - 1);
for (int i = 0; i < argc - 1; i++)
{
params[i] = shellExtParsePara(shell, argv[i + 1]);
}
switch (paramNum)
{
#if SHELL_PARAMETER_MAX_NUMBER >= 1
case 0:
return command->data.cmd.function();
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 1 */
#if SHELL_PARAMETER_MAX_NUMBER >= 2
case 1:
return command->data.cmd.function(params[0]);
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 2 */
#if SHELL_PARAMETER_MAX_NUMBER >= 3
case 2:
return command->data.cmd.function(params[0], params[1]);
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 3 */
#if SHELL_PARAMETER_MAX_NUMBER >= 4
case 3:
return command->data.cmd.function(params[0], params[1],
params[2]);
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 4 */
#if SHELL_PARAMETER_MAX_NUMBER >= 5
case 4:
return command->data.cmd.function(params[0], params[1],
params[2], params[3]);
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 5 */
#if SHELL_PARAMETER_MAX_NUMBER >= 6
case 5:
return command->data.cmd.function(params[0], params[1],
params[2], params[3],
params[4]);
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 6 */
#if SHELL_PARAMETER_MAX_NUMBER >= 7
case 6:
return command->data.cmd.function(params[0], params[1],
params[2], params[3],
params[4], params[5]);
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 7 */
#if SHELL_PARAMETER_MAX_NUMBER >= 8
case 7:
return command->data.cmd.function(params[0], params[1],
params[2], params[3],
params[4], params[5],
params[6]);
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 8 */
#if SHELL_PARAMETER_MAX_NUMBER >= 9
case 8:
return command->data.cmd.function(params[0], params[1],
params[2], params[3],
params[4], params[5],
params[6], params[7]);
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 9 */
#if SHELL_PARAMETER_MAX_NUMBER >= 10
case 9:
return command->data.cmd.function(params[0], params[1],
params[2], params[3],
params[4], params[5],
params[6], params[7],
params[8]);
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 10 */
#if SHELL_PARAMETER_MAX_NUMBER >= 11
case 10:
return command->data.cmd.function(params[0], params[1],
params[2], params[3],
params[4], params[5],
params[6], params[7],
params[8], params[9]);
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 11 */
#if SHELL_PARAMETER_MAX_NUMBER >= 12
case 11:
return command->data.cmd.function(params[0], params[1],
params[2], params[3],
params[4], params[5],
params[6], params[7],
params[8], params[9],
params[10]);
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 12 */
#if SHELL_PARAMETER_MAX_NUMBER >= 13
case 12:
return command->data.cmd.function(params[0], params[1],
params[2], params[3],
params[4], params[5],
params[6], params[7],
params[8], params[9],
params[10], params[11]);
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 13 */
#if SHELL_PARAMETER_MAX_NUMBER >= 14
case 13:
return command->data.cmd.function(params[0], params[1],
params[2], params[3],
params[4], params[5],
params[6], params[7],
params[8], params[9],
params[10], params[11],
params[12]);
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 14 */
#if SHELL_PARAMETER_MAX_NUMBER >= 15
case 14:
return command->data.cmd.function(params[0], params[1],
params[2], params[3],
params[4], params[5],
params[6], params[7],
params[8], params[9],
params[10], params[11],
params[12], params[13]);
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 15 */
#if SHELL_PARAMETER_MAX_NUMBER >= 16
case 15:
return command->data.cmd.function(params[0], params[1],
params[2], params[3],
params[4], params[5],
params[6], params[7],
params[8], params[9],
params[10], params[11],
params[12], params[13],
params[14]);
// break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 16 */
default:
return -1;
// break;
}
}
+33
View File
@@ -0,0 +1,33 @@
/**
* @file shell_ext.h
* @author Letter (NevermindZZT@gmail.com)
* @brief shell extensions
* @version 3.0.0
* @date 2019-12-31
*
* @copyright (c) 2019 Letter
*
*/
#ifndef __SHELL_EXT_H__
#define __SHELL_EXT_H__
#include "shell.h"
/**
* @brief 数字类型
*
*/
typedef enum
{
NUM_TYPE_DEC, /**< 十进制整型 */
NUM_TYPE_BIN, /**< 二进制整型 */
NUM_TYPE_OCT, /**< 八进制整型 */
NUM_TYPE_HEX, /**< 十六进制整型 */
NUM_TYPE_FLOAT /**< 浮点型 */
} ShellNumType;
unsigned int shellExtParsePara(Shell *shell, char *string);
int shellExtRun(Shell *shell, ShellCommand *command, int argc, char *argv[]);
#endif
@@ -0,0 +1,84 @@
#!/usr/bin/python
# -*- coding:UTF-8 -*-
"""
shellTools
Author
Letter(nevermindzzt@gmail.com)
Date
2020-04-14
Copyright
(c) Letter 2020
"""
import os
import sys
import re
FILE_PATTERN = r"^.*?\.(cpp|c|h)$"
SHELL_EXPORT_PATTERN = r"^\s*SHELL_EXPORT_(CMD|CMD_AGENCY|VAR|USER|KEY|KEY_AGENCY|CMD_GROUP|PASSTROUGH)\s*\(\s*\S+\s*,\s*(\S+)\s*,.*$"
def isCodeFile(name):
return re.match(FILE_PATTERN, name) != None
def getCodeFiles(path):
names = os.listdir(path)
files = []
for name in names:
fullName = os.path.join(path, name)
if os.path.isdir(fullName):
files = files + getCodeFiles(fullName)
else:
if re.match(FILE_PATTERN, name) != None:
files.append(fullName)
return files
def getFileEcoding(name):
encoding = ""
try:
with open(name, encoding="utf-8") as f:
f.read()
encoding = "utf-8"
except:
try:
with open(name, encoding="gbk") as f:
f.read()
encoding = "gbk"
except:
encoding = "ascii"
return encoding
if __name__ == "__main__":
path = sys.argv[1]
itemList = []
codeLine = ""
for codeFile in getCodeFiles(path):
try:
with open(codeFile, encoding=getFileEcoding(codeFile)) as f:
lineNum = 0
while True:
line = f.readline()
if line:
line = line.replace("\r", " ").replace("\n", " ")
codeLine = codeLine + line
lineNum = lineNum + 1
if ";" in codeLine or "}" in codeLine:
matcher = re.match(SHELL_EXPORT_PATTERN, codeLine)
if matcher != None:
item = (matcher.group(1), matcher.group(2), codeFile, lineNum)
itemList.append(item)
codeLine = ""
else:
codeLine = ""
break
except Exception as e:
print(e)
pass
for item in sorted(itemList, key=lambda x:(x[1])):
print("shell export %s, %s in file %s:%d"
%(item[0], item[1], item[2], item[3]))