修复appfs对vi的支持
This commit is contained in:
10
TODO.md
10
TODO.md
@@ -11,14 +11,16 @@
|
||||
* [ ] 文件系统&网络协议栈完善自动删除支持(文件描述符自动管理库)
|
||||
* [x] 内核信号量对象完善,支持超时(已完成)。
|
||||
* [ ] 内核信号量支持优先级反转
|
||||
* [ ] FPU完善支持,目前版本偶发压栈错误
|
||||
* [ ] console 支持自动切换前后台
|
||||
* [x] TTY 支持自动切换前后台
|
||||
* [x] 重构路径管理(nsfs)
|
||||
* [ ] 线程占用率统计
|
||||
* [x] 去除原来的ipc机制(使用fastipc机制),并单独实现sleep接口,目前的ipc有概率卡死问题
|
||||
* [ ] 几大组件稳定性测试
|
||||
* [ ] dup, dup2等接口支持
|
||||
* [ ] fastipc FPU支持
|
||||
* [ ] FPU完整支持,fastipc FPU支持
|
||||
* [x] TTY驱动支持
|
||||
* [ ] proc支持
|
||||
* [ ] 新进程中env支持
|
||||
### mid prio
|
||||
* [x] net server support
|
||||
* [x] block driver
|
||||
@@ -29,11 +31,13 @@
|
||||
* [x] pin drvier
|
||||
* [x] snd drvier
|
||||
* [ ] ymodem support
|
||||
* [x] vi support
|
||||
### low prio
|
||||
- [ ] toybox support
|
||||
- [ ] ota support
|
||||
- [ ] lvgl support
|
||||
- [ ] modbus support
|
||||
- [ ] poll/select实现
|
||||
|
||||
#### must low prio
|
||||
- [ ] AT proctol support
|
||||
|
||||
@@ -6,6 +6,7 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} \
|
||||
-Wno-unused-function \
|
||||
-Wno-unused-variable \
|
||||
-Wno-builtin-declaration-mismatch \
|
||||
-Ofast \
|
||||
")
|
||||
|
||||
# message(编译参数:${CMAKE_C_FLAGS})
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
ENTRY(Reset_Handler)
|
||||
MEMORY
|
||||
{
|
||||
RAM (arw) : ORIGIN = 0x20000000, LENGTH = 0x2000000
|
||||
FLASH (arx) : ORIGIN = 0x8000000 + 0x2000 + 0x2000, LENGTH = 0x1C000
|
||||
RAM (arw) : ORIGIN = 0x20000000, LENGTH = 0x60000
|
||||
FLASH (arx) : ORIGIN = 0x8000000 + 0x3000 + 0x1000, LENGTH = 0x10000
|
||||
}
|
||||
SECTIONS
|
||||
{
|
||||
|
||||
@@ -48,7 +48,7 @@ int ls(int argc, char *agrv[])
|
||||
while ((ptr = readdir(dir)) != NULL)
|
||||
{
|
||||
struct stat st = {0};
|
||||
strcat(path, "/");
|
||||
// strcat(path, "/");
|
||||
strcat(path, ptr->d_name);
|
||||
ret = stat(path, &st);
|
||||
// if (ret >= 0)
|
||||
@@ -238,3 +238,10 @@ int shell_sys_info(int argc, char *argv[])
|
||||
return 0;
|
||||
}
|
||||
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), sys, shell_sys_info, sys command);
|
||||
|
||||
int shell_exit(int argc, char *argv[])
|
||||
{
|
||||
exit(0);
|
||||
return 0;
|
||||
}
|
||||
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), exit, exit, exit command);
|
||||
|
||||
@@ -7,6 +7,8 @@ typedef struct u_mutex
|
||||
obj_handler_t obj;
|
||||
} u_mutex_t;
|
||||
|
||||
#define U_MUTEX_INIT {.obj = HANDLER_INVALID}
|
||||
|
||||
int u_mutex_init(u_mutex_t *lock, obj_handler_t sema_hd);
|
||||
void u_mutex_lock(u_mutex_t *lock, umword_t timeout, umword_t *remain_times);
|
||||
void u_mutex_unlock(u_mutex_t *lock);
|
||||
|
||||
@@ -9,5 +9,6 @@ struct allocmem
|
||||
|
||||
void *u_malloc(size_t size);
|
||||
void *u_calloc(size_t size, int nmemb);
|
||||
void *u_realloc(void *old, size_t size);
|
||||
void u_free(void *ptr);
|
||||
// void spurge();
|
||||
|
||||
@@ -7,10 +7,34 @@
|
||||
#include <sys/shm.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <u_malloc.h>
|
||||
#include <u_mutex.h>
|
||||
#include <u_hd_man.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
static struct allocmem *memlocs = NULL;
|
||||
static u_mutex_t lock_mutex = U_MUTEX_INIT;
|
||||
|
||||
static void alloc_lock(void)
|
||||
{
|
||||
obj_handler_t lock_hd;
|
||||
|
||||
if (lock_mutex.obj == HANDLER_INVALID)
|
||||
{
|
||||
lock_hd = handler_alloc();
|
||||
assert(lock_hd != HANDLER_INVALID);
|
||||
u_mutex_init(&lock_mutex, lock_hd);
|
||||
}
|
||||
u_mutex_lock(&lock_mutex, 0, NULL);
|
||||
}
|
||||
static void alloc_unlock(void)
|
||||
{
|
||||
assert(lock_mutex.obj != HANDLER_INVALID);
|
||||
u_mutex_unlock(&lock_mutex);
|
||||
}
|
||||
|
||||
static struct allocmem *new_alloc_mem()
|
||||
{
|
||||
@@ -83,10 +107,13 @@ struct allocmem *find_and_remove(struct allocmem *ptr, void *searchPtr)
|
||||
void *u_malloc(size_t size)
|
||||
{
|
||||
struct allocmem *info = NULL;
|
||||
|
||||
alloc_lock();
|
||||
memlocs = insert_end(memlocs, &info);
|
||||
if (info == NULL)
|
||||
{
|
||||
/// Failed to initialize metadata info
|
||||
alloc_unlock();
|
||||
return NULL;
|
||||
}
|
||||
void *ptr;
|
||||
@@ -101,8 +128,10 @@ void *u_malloc(size_t size)
|
||||
info->ptr = ptr;
|
||||
info->size = size;
|
||||
/// Return this precious pointer
|
||||
alloc_unlock();
|
||||
return ptr;
|
||||
}
|
||||
alloc_unlock();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -127,16 +156,44 @@ void *u_calloc(size_t size, int nmemb)
|
||||
}
|
||||
return newPtr;
|
||||
}
|
||||
void *u_realloc(void *old, size_t size)
|
||||
{
|
||||
struct allocmem *info;
|
||||
size_t old_size;
|
||||
void *new_mem = u_malloc(size);
|
||||
|
||||
if (!new_mem)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
alloc_lock();
|
||||
info = find(memlocs, old);
|
||||
if (info == NULL)
|
||||
{
|
||||
alloc_unlock();
|
||||
u_free(new_mem);
|
||||
return NULL;
|
||||
}
|
||||
old_size = info->size;
|
||||
alloc_unlock();
|
||||
memcpy(new_mem, old, old_size);
|
||||
u_free(old);
|
||||
return new_mem;
|
||||
}
|
||||
void u_free(void *ptr)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
alloc_lock();
|
||||
struct allocmem *info = find(memlocs, ptr);
|
||||
if (info == NULL)
|
||||
{
|
||||
alloc_unlock();
|
||||
return;
|
||||
}
|
||||
munmap(info->ptr, info->size);
|
||||
memlocs = find_and_remove(memlocs, ptr);
|
||||
alloc_unlock();
|
||||
}
|
||||
|
||||
/// Violently Remove Everything
|
||||
|
||||
@@ -4,4 +4,5 @@ cmake_minimum_required(VERSION 3.13)
|
||||
# add_subdirectory(cpiofs)
|
||||
add_subdirectory(fatfs)
|
||||
add_subdirectory(appfs)
|
||||
# add_subdirectory(procfs)
|
||||
# add_subdirectory(lxext4)
|
||||
|
||||
@@ -337,7 +337,7 @@ int appfs_create_file(fs_info_t *info, const char *path, int size)
|
||||
ret = appfs_check_file_exist(info, path);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
for (int i = 0; i < info->save.dirinfo_nr; i++)
|
||||
@@ -430,6 +430,8 @@ int appfs_write_file(fs_info_t *info, const char *name, void *data, int size, in
|
||||
// 写入数据
|
||||
int write_size = MIN(size, info->save.block_size - (offset % info->save.block_size));
|
||||
|
||||
write_size = MIN(dir_info->size - offset, write_size);
|
||||
|
||||
if (write_size != info->save.block_size)
|
||||
{
|
||||
ret = info->cb.hw_read_block(info, block_inx + k + block_offset_inx, info->buf, info->save.block_size);
|
||||
@@ -479,6 +481,7 @@ int appfs_read_file(fs_info_t *info, const char *name, void *data, int size, int
|
||||
// 写入数据
|
||||
int read_size = MIN(size, info->save.block_size - (offset % info->save.block_size));
|
||||
|
||||
read_size = MIN(dir_info->size - offset, read_size);
|
||||
ret = info->cb.hw_read_block(info, block_inx + k + block_offset_inx, info->buf, info->save.block_size);
|
||||
if (ret < 0)
|
||||
{
|
||||
|
||||
@@ -163,7 +163,8 @@ int appfs_open(const char *name, int flags, int mode)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (name[0] == '/') {
|
||||
if (name[0] == '/')
|
||||
{
|
||||
name++;
|
||||
}
|
||||
type = APPFS_FILE_TYPE;
|
||||
@@ -176,7 +177,10 @@ int appfs_open(const char *name, int flags, int mode)
|
||||
}
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
if (ret != -EEXIST)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
file = appfs_find_file_by_name(fs, name);
|
||||
if (file == NULL)
|
||||
@@ -430,6 +434,10 @@ int appfs_stat(const char *path, struct kstat *st)
|
||||
st->st_nlink = 0;
|
||||
return 0;
|
||||
}
|
||||
if (path[0] == '/')
|
||||
{
|
||||
path++;
|
||||
}
|
||||
file = appfs_find_file_by_name(fs, path);
|
||||
if (file == NULL)
|
||||
{
|
||||
|
||||
@@ -2,8 +2,34 @@
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include "appfs.h"
|
||||
#ifdef MKRTOS
|
||||
#include "kstat.h"
|
||||
#include <u_types.h>
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
struct kstat {
|
||||
long st_dev;
|
||||
int __st_dev_padding;
|
||||
long __st_ino_truncated;
|
||||
mode_t st_mode;
|
||||
nlink_t st_nlink;
|
||||
uid_t st_uid;
|
||||
gid_t st_gid;
|
||||
long st_rdev;
|
||||
int __st_rdev_padding;
|
||||
long st_size;
|
||||
int st_blksize;
|
||||
long st_blocks;
|
||||
long st_atime_sec;
|
||||
long st_atime_nsec;
|
||||
long st_mtime_sec;
|
||||
long st_mtime_nsec;
|
||||
long st_ctime_sec;
|
||||
long st_ctime_nsec;
|
||||
long st_ino;
|
||||
};
|
||||
|
||||
#endif
|
||||
enum appfs_ioctl_cmd_op
|
||||
{
|
||||
APPFS_IOCTOL_GET_ACCESS_ADDR,
|
||||
|
||||
63
mkrtos_user/server/fs/procfs/CMakeLists.txt
Normal file
63
mkrtos_user/server/fs/procfs/CMakeLists.txt
Normal file
@@ -0,0 +1,63 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
file(
|
||||
GLOB deps
|
||||
src/*.c
|
||||
)
|
||||
|
||||
add_executable(
|
||||
procfs.elf
|
||||
${deps}
|
||||
${START_SRC}
|
||||
)
|
||||
target_link_libraries(
|
||||
procfs.elf
|
||||
PUBLIC
|
||||
-Bstatic
|
||||
${LIBC_NAME}
|
||||
${START_LIB}
|
||||
sys
|
||||
sys_util
|
||||
sys_svr
|
||||
fd
|
||||
${GCC_LIB_PATH}/libgcc.a
|
||||
)
|
||||
target_include_directories(
|
||||
procfs.elf
|
||||
PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/mkrtos_user/lib/sys/inc
|
||||
${CMAKE_SOURCE_DIR}/mkrtos_user/lib/sys_svr/inc
|
||||
|
||||
${CMAKE_SOURCE_DIR}/mkrtos_user/server/fs/procfs/inc
|
||||
${CMAKE_SOURCE_DIR}/mkrtos_user/server/fs/procfs
|
||||
|
||||
${CMAKE_SOURCE_DIR}/mkrtos_user/lib/fd
|
||||
)
|
||||
add_dependencies(
|
||||
procfs.elf
|
||||
${START_LIB}
|
||||
sys
|
||||
sys_util
|
||||
fd
|
||||
)
|
||||
set_target_properties(
|
||||
procfs.elf PROPERTIES LINK_FLAGS
|
||||
"-T ${CMAKE_CURRENT_LIST_DIR}/${ARCH_NAME}/link.lds ${CORTEX_M_LINK_FLAGS} --gc-section -no-dynamic-linker "
|
||||
#--no-warn-rwx-segments
|
||||
)
|
||||
add_custom_target(
|
||||
procfs_dump ALL
|
||||
COMMAND
|
||||
${CMAKE_OBJCOPY} -O binary -S procfs.elf procfs.bin
|
||||
COMMAND
|
||||
${CMAKE_SIZE} procfs.elf
|
||||
COMMAND
|
||||
mkdir -p ${CMAKE_SOURCE_DIR}/build/output/cpio
|
||||
COMMAND
|
||||
cp procfs.bin ${CMAKE_SOURCE_DIR}/build/output/cpio/procfs
|
||||
COMMAND
|
||||
cp procfs.elf ${CMAKE_SOURCE_DIR}/build/output/procfs.elf
|
||||
)
|
||||
|
||||
add_dependencies(procfs_dump procfs.elf)
|
||||
|
||||
2
mkrtos_user/server/fs/procfs/Readme.md
Normal file
2
mkrtos_user/server/fs/procfs/Readme.md
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
procfs只是一个库,用于集成到每个应用中。每个应用都可以作为一个文件系统,对外提供控制方法。
|
||||
0
mkrtos_user/server/fs/procfs/src/dummy.c
Normal file
0
mkrtos_user/server/fs/procfs/src/dummy.c
Normal file
@@ -1,6 +1,6 @@
|
||||
|
||||
#define HEAP_SIZE (128*1024)
|
||||
#define STACK_SIZE (1024 * 3)
|
||||
#define HEAP_SIZE (512)
|
||||
#define STACK_SIZE (1024 * 2)
|
||||
|
||||
#if defined(__CC_ARM)
|
||||
#define HEAP_ATTR SECTION("HEAP") __attribute__((zero_init))
|
||||
|
||||
@@ -364,7 +364,7 @@ struct globals
|
||||
{ \
|
||||
FREE_PTR_TO_GLOBALS(); \
|
||||
/* "" but has space for 2 chars: */ \
|
||||
IF_FEATURE_VI_SEARCH(free(last_search_pattern);) \
|
||||
IF_FEATURE_VI_SEARCH(u_free(last_search_pattern);) \
|
||||
} while (0)
|
||||
|
||||
static void edit_file(char *); // edit one file
|
||||
@@ -592,11 +592,11 @@ int vi_main(int argc, char **argv)
|
||||
|
||||
/* RT-Thread team added */
|
||||
fflush_all();
|
||||
free(text);
|
||||
free(screen);
|
||||
free(current_filename);
|
||||
u_free(text);
|
||||
u_free(screen);
|
||||
u_free(current_filename);
|
||||
#if ENABLE_FEATURE_VI_DOT_CMD
|
||||
free(ioq_start);
|
||||
u_free(ioq_start);
|
||||
#endif
|
||||
DELETE_G();
|
||||
return 0;
|
||||
@@ -617,13 +617,13 @@ static int init_text_buffer(char *fn)
|
||||
#endif
|
||||
|
||||
/* allocate/reallocate text buffer */
|
||||
free(text);
|
||||
u_free(text);
|
||||
text_size = 10240;
|
||||
screenbegin = dot = end = text = xzalloc(text_size);
|
||||
|
||||
if (fn != current_filename)
|
||||
{
|
||||
free(current_filename);
|
||||
u_free(current_filename);
|
||||
current_filename = xstrdup(fn);
|
||||
}
|
||||
rc = file_insert(fn, text, 1);
|
||||
@@ -720,7 +720,7 @@ static void edit_file(char *fn)
|
||||
offset = 0; // no horizontal offset
|
||||
c = '\0';
|
||||
#if ENABLE_FEATURE_VI_DOT_CMD
|
||||
free(ioq_start);
|
||||
u_free(ioq_start);
|
||||
ioq_start = NULL;
|
||||
lmc_len = 0;
|
||||
adding2q = 0;
|
||||
@@ -743,7 +743,7 @@ static void edit_file(char *fn)
|
||||
if (*q)
|
||||
colon(q);
|
||||
} while (p);
|
||||
free(initial_cmds[n]);
|
||||
u_free(initial_cmds[n]);
|
||||
initial_cmds[n] = NULL;
|
||||
n++;
|
||||
}
|
||||
@@ -855,7 +855,7 @@ static char *get_one_address(char *p, int *addr) // get colon addr, if present
|
||||
if (p + 1 != q)
|
||||
{
|
||||
// save copy of new pattern
|
||||
free(last_search_pattern);
|
||||
u_free(last_search_pattern);
|
||||
last_search_pattern = xstrndup(p, q - p);
|
||||
}
|
||||
p = q;
|
||||
@@ -1181,11 +1181,11 @@ static void colon(char *buf)
|
||||
#if ENABLE_FEATURE_VI_YANKMARK
|
||||
if (Ureg >= 0 && Ureg < 28)
|
||||
{
|
||||
free(reg[Ureg]); // free orig line reg- for 'U'
|
||||
u_free(reg[Ureg]); // u_free orig line reg- for 'U'
|
||||
reg[Ureg] = NULL;
|
||||
}
|
||||
/*if (YDreg < 28) - always true*/ {
|
||||
free(reg[YDreg]); // free default yank/delete register
|
||||
u_free(reg[YDreg]); // u_free default yank/delete register
|
||||
reg[YDreg] = NULL;
|
||||
}
|
||||
#endif
|
||||
@@ -1209,7 +1209,7 @@ static void colon(char *buf)
|
||||
if (args[0])
|
||||
{
|
||||
// user wants a new filename
|
||||
free(current_filename);
|
||||
u_free(current_filename);
|
||||
current_filename = xstrdup(args);
|
||||
}
|
||||
else
|
||||
@@ -1958,7 +1958,7 @@ static void new_screen(int ro, int co)
|
||||
{
|
||||
char *s;
|
||||
|
||||
free(screen);
|
||||
u_free(screen);
|
||||
screensize = ro * co + 8;
|
||||
s = screen = xmalloc(screensize);
|
||||
// initialize the new screen. assume this will be a empty file.
|
||||
@@ -2413,7 +2413,7 @@ static void flush_undo_data(void)
|
||||
{
|
||||
undo_entry = undo_stack_tail;
|
||||
undo_stack_tail = undo_entry->prev;
|
||||
free(undo_entry);
|
||||
u_free(undo_entry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2605,7 +2605,7 @@ static void undo_pop(void)
|
||||
}
|
||||
// Deallocate the undo object we just processed
|
||||
undo_stack_tail = undo_entry->prev;
|
||||
free(undo_entry);
|
||||
u_free(undo_entry);
|
||||
modified_count--;
|
||||
// For chained operations, continue popping all the way down the chain.
|
||||
if (repeat)
|
||||
@@ -2853,7 +2853,7 @@ static char *text_yank(char *p, char *q, int dest, int buftype)
|
||||
p = q;
|
||||
cnt = -cnt;
|
||||
}
|
||||
free(reg[dest]); // if already a yank register, free it
|
||||
u_free(reg[dest]); // if already a yank register, u_free it
|
||||
reg[dest] = xstrndup(p, cnt + 1);
|
||||
return p;
|
||||
}
|
||||
@@ -3031,7 +3031,7 @@ static int get_one_char(void)
|
||||
if (c != '\0')
|
||||
return c;
|
||||
// the end of the q
|
||||
free(ioq_start);
|
||||
u_free(ioq_start);
|
||||
ioq_start = NULL;
|
||||
// read from STDIN:
|
||||
}
|
||||
@@ -4076,7 +4076,7 @@ key_cmd_mode:
|
||||
if (q[0])
|
||||
{ // strlen(q) > 1: new pat- save it and find
|
||||
// there is a new pat
|
||||
free(last_search_pattern);
|
||||
u_free(last_search_pattern);
|
||||
last_search_pattern = xstrdup(q);
|
||||
goto dc3; // now find the pattern
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ int safe_poll(struct pollfd *ufds, nfds_t nfds, int timeout)
|
||||
|
||||
void *xzalloc(size_t size)
|
||||
{
|
||||
void *ptr = malloc(size);
|
||||
void *ptr = u_malloc(size);
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
// #include <dfs_poll.h>
|
||||
#include <poll.h>
|
||||
#include <sys/types.h>
|
||||
#include <u_malloc.h>
|
||||
|
||||
#define BB_VER "latest: 2021-04-07"
|
||||
#define BB_BT "busybox vi"
|
||||
@@ -262,7 +263,7 @@
|
||||
} while (0)
|
||||
#define FREE_PTR_TO_GLOBALS() do { \
|
||||
if (ENABLE_FEATURE_CLEAN_UP) { \
|
||||
free(ptr_to_globals); \
|
||||
u_free(ptr_to_globals); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
@@ -338,11 +339,26 @@ int isatty (int fd);
|
||||
/* At least gcc 3.4.6 on mipsel system needs optimization barrier */
|
||||
#define barrier() __asm__ __volatile__("":::"memory")
|
||||
#endif
|
||||
|
||||
#define xmalloc malloc
|
||||
#define xrealloc realloc
|
||||
#define xstrdup strdup
|
||||
#define xstrndup strndup
|
||||
static inline char *u_strdup(const char *s)
|
||||
{
|
||||
size_t l = strlen(s);
|
||||
char *d = u_malloc(l+1);
|
||||
if (!d) return NULL;
|
||||
return memcpy(d, s, l+1);
|
||||
}
|
||||
static inline char *u_strndup(const char *s, size_t n)
|
||||
{
|
||||
size_t l = strnlen(s, n);
|
||||
char *d = u_malloc(l+1);
|
||||
if (!d) return NULL;
|
||||
memcpy(d, s, l);
|
||||
d[l] = 0;
|
||||
return d;
|
||||
}
|
||||
#define xmalloc u_malloc
|
||||
#define xrealloc u_realloc
|
||||
#define xstrdup u_strdup
|
||||
#define xstrndup u_strndup
|
||||
#define bb_putchar putchar
|
||||
#define bb_strtou strtoul
|
||||
#define bb_simple_error_msg_and_die(...) printf(__VA_ARGS__)
|
||||
|
||||
Reference in New Issue
Block a user