initial commit
This commit is contained in:
42
.gitignore
vendored
Normal file
42
.gitignore
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/cmake,visualstudiocode
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=cmake,visualstudiocode
|
||||||
|
|
||||||
|
### CMake ###
|
||||||
|
CMakeLists.txt.user
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles
|
||||||
|
CMakeScripts
|
||||||
|
Testing
|
||||||
|
Makefile
|
||||||
|
cmake_install.cmake
|
||||||
|
install_manifest.txt
|
||||||
|
compile_commands.json
|
||||||
|
CTestTestfile.cmake
|
||||||
|
_deps
|
||||||
|
|
||||||
|
### CMake Patch ###
|
||||||
|
# External projects
|
||||||
|
*-prefix/
|
||||||
|
|
||||||
|
### VisualStudioCode ###
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/*.code-snippets
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# Built Visual Studio Code Extensions
|
||||||
|
*.vsix
|
||||||
|
|
||||||
|
### VisualStudioCode Patch ###
|
||||||
|
# Ignore all local history of files
|
||||||
|
.history
|
||||||
|
.ionide
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/cmake,visualstudiocode
|
||||||
|
|
||||||
|
build/*
|
||||||
25
CMakeLists.txt
Normal file
25
CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
project(hx-serial)
|
||||||
|
|
||||||
|
# 设置源文件
|
||||||
|
set(HX_SERIAL hx_serial.c)
|
||||||
|
set(LOGC logc/log.c)
|
||||||
|
|
||||||
|
# 设置彩色日志输出
|
||||||
|
add_definitions(-DLOG_USE_COLOR)
|
||||||
|
|
||||||
|
# 添加共享库
|
||||||
|
add_library(hx-serial SHARED ${HX_SERIAL} ${LOGC})
|
||||||
|
|
||||||
|
# 链接 pthread 库
|
||||||
|
target_link_libraries(hx-serial pthread)
|
||||||
|
|
||||||
|
# 安装目标
|
||||||
|
install(TARGETS hx-serial
|
||||||
|
LIBRARY DESTINATION lib
|
||||||
|
ARCHIVE DESTINATION lib)
|
||||||
|
|
||||||
|
# 安装头文件
|
||||||
|
install(FILES hx_serial.h
|
||||||
|
DESTINATION include)
|
||||||
219
hx_serial.c
Normal file
219
hx_serial.c
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
#include "hx_serial.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include "logc/log.h"
|
||||||
|
|
||||||
|
int by_serial_init(by_serial_t *serial_port, const char *dev_name)
|
||||||
|
{
|
||||||
|
serial_port->dev_name_len = strlen(dev_name);
|
||||||
|
assert(0 < serial_port->dev_name_len);
|
||||||
|
strcpy(serial_port->dev_name_str, dev_name);
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
fd = open(serial_port->dev_name_str, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
|
||||||
|
if (fd < 0) {
|
||||||
|
log_error("open uart device error");
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
serial_port->fd = fd;
|
||||||
|
by_serial_set_baudrate(serial_port, 115200);
|
||||||
|
by_serial_set_parity(serial_port, 8, 1, 'N');
|
||||||
|
log_info("serial %s init successful", serial_port->dev_name_str);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int by_serial_set_baudrate(by_serial_t *serial_port, int baudrate)
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
int status = 0;
|
||||||
|
int speed_arr[] = {
|
||||||
|
B115200,
|
||||||
|
B38400,
|
||||||
|
B19200,
|
||||||
|
B9600,
|
||||||
|
B4800,
|
||||||
|
B2400,
|
||||||
|
B1200};
|
||||||
|
int name_arr[] = {
|
||||||
|
115200,
|
||||||
|
38400,
|
||||||
|
19200,
|
||||||
|
9600,
|
||||||
|
4800,
|
||||||
|
2400,
|
||||||
|
1200};
|
||||||
|
struct termios Opt = {0};
|
||||||
|
|
||||||
|
tcgetattr(serial_port->fd, &Opt);
|
||||||
|
for (index = 0; index < sizeof(speed_arr) / sizeof(int); index++) {
|
||||||
|
if (baudrate == name_arr[index]) {
|
||||||
|
|
||||||
|
/*设置前 flush*/
|
||||||
|
tcflush(serial_port->fd, TCIOFLUSH);
|
||||||
|
cfsetispeed(&Opt, speed_arr[index]);
|
||||||
|
cfsetospeed(&Opt, speed_arr[index]);
|
||||||
|
|
||||||
|
/*tcsetattr(串口描述符,立即使用或者其他标示,指向 termios 的指针),通过
|
||||||
|
tcsetattr 函数把新的属性设置到串口上。*/
|
||||||
|
status = tcsetattr(serial_port->fd, TCSANOW, &Opt);
|
||||||
|
if (0 != status) {
|
||||||
|
log_error("tcsetattr COM error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*设置后 flush*/
|
||||||
|
tcflush(serial_port->fd, TCIOFLUSH);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int by_serial_set_parity(by_serial_t *serial_port, int databits, int stopbits, char parity)
|
||||||
|
{
|
||||||
|
struct termios Opt = {0};
|
||||||
|
|
||||||
|
if (0 != tcgetattr(serial_port->fd, &Opt)) {
|
||||||
|
log_error("tcgetattr COM error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/
|
||||||
|
Opt.c_oflag &= ~OPOST; /*Output*/
|
||||||
|
|
||||||
|
Opt.c_iflag &= ~ICRNL; // 禁止将输入的 CR 转换为 NL
|
||||||
|
Opt.c_iflag &= ~(IXON); // 清 bit 位 关闭流控字符
|
||||||
|
|
||||||
|
/*设置数据位,取值为 7 或 8*/
|
||||||
|
Opt.c_cflag &= ~CSIZE;
|
||||||
|
switch (databits) {
|
||||||
|
case 7:
|
||||||
|
Opt.c_cflag |= CS7;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
Opt.c_cflag |= CS8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log_error("Unsupported data size");
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*设置停止位,取值为 1 或 2*/
|
||||||
|
switch (stopbits) {
|
||||||
|
case 1:
|
||||||
|
Opt.c_cflag &= ~CSTOPB;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Opt.c_cflag |= CSTOPB;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log_error("Unsupported stop bits");
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*设置校验位,取值为 E,N,O,S*/
|
||||||
|
switch (parity) {
|
||||||
|
case 'e':
|
||||||
|
case 'E': {
|
||||||
|
Opt.c_cflag |= PARENB; /*Enable parity*/
|
||||||
|
Opt.c_cflag &= ~PARODD; /*转换为偶效验*/
|
||||||
|
Opt.c_iflag |= INPCK; /*Disnable parity checking*/
|
||||||
|
} break;
|
||||||
|
case 'n':
|
||||||
|
case 'N': {
|
||||||
|
Opt.c_cflag &= ~PARENB; /*Clear parity enable*/
|
||||||
|
Opt.c_iflag &= ~INPCK; /*Enable parity checking*/
|
||||||
|
} break;
|
||||||
|
case 'o':
|
||||||
|
case 'O': {
|
||||||
|
Opt.c_cflag |= (PARODD | PARENB); /*设置为奇效验*/
|
||||||
|
Opt.c_iflag |= INPCK; /*Disnable parity checking*/
|
||||||
|
} break;
|
||||||
|
case 's': /*as no parity*/
|
||||||
|
case 'S': /*as no parity*/
|
||||||
|
{
|
||||||
|
Opt.c_cflag &= ~PARENB;
|
||||||
|
Opt.c_cflag &= ~CSTOPB;
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
log_error("Unsupported parity\n");
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*设置结构体输入校验位*/
|
||||||
|
if ('n' != parity) {
|
||||||
|
Opt.c_iflag |= INPCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
tcflush(serial_port->fd, TCIFLUSH);
|
||||||
|
Opt.c_cc[VTIME] = 150; /*设置超时 15 秒*/
|
||||||
|
Opt.c_cc[VMIN] = 0; /*更新结构体并立即执行*/
|
||||||
|
if (0 != tcsetattr(serial_port->fd, TCSANOW, &Opt)) {
|
||||||
|
log_error("tcsetattr COM error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int by_serial_write(by_serial_t *serial_port, const char *buff, const int len)
|
||||||
|
{
|
||||||
|
int len_t;
|
||||||
|
|
||||||
|
if (serial_port->fd < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
len_t = write(serial_port->fd, buff, len);
|
||||||
|
|
||||||
|
if (0 > len_t) {
|
||||||
|
// log_error("write buff failed");
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
// printf("write buff successful %d/%d\r\n", len_t, len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int by_serial_read(by_serial_t *serial_port, char *buff, const int len)
|
||||||
|
{
|
||||||
|
if (1 > by_serial_get_used_buffer_len(serial_port)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int len_t = -1;
|
||||||
|
|
||||||
|
if (serial_port->fd < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
len_t = read(serial_port->fd, buff, len);
|
||||||
|
|
||||||
|
if (0 > len_t) {
|
||||||
|
// log_error("read buff failed");
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
// printf("read buff successful %d/%d\r\n", len_t, len);
|
||||||
|
return len_t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int by_serial_get_used_buffer_len(by_serial_t *serial_port)
|
||||||
|
{
|
||||||
|
int used_len = 0;
|
||||||
|
ioctl(serial_port->fd, FIONREAD, &used_len);
|
||||||
|
return used_len;
|
||||||
|
}
|
||||||
33
hx_serial.h
Normal file
33
hx_serial.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef _BY_SERIAL_H__
|
||||||
|
#define _BY_SERIAL_H__
|
||||||
|
|
||||||
|
#define BY_SERIAL_DEV_STATUS_STDBY 0x00
|
||||||
|
#define BY_SERIAL_DEV_STATUS_ERROR 0xFF
|
||||||
|
#define BY_SERIAL_DEV_STATUS_OPEND 0x01
|
||||||
|
|
||||||
|
typedef struct by_serial_t {
|
||||||
|
int fd;
|
||||||
|
char dev_name_str[40];
|
||||||
|
int dev_name_len;
|
||||||
|
int dev_status;
|
||||||
|
} by_serial_t;
|
||||||
|
|
||||||
|
/* Open serial device. */
|
||||||
|
extern int by_serial_init(by_serial_t *serial_port, const char *dev_name);
|
||||||
|
|
||||||
|
/* Set baudrate. */
|
||||||
|
extern int by_serial_set_baudrate(by_serial_t *serial_port, int baudrate);
|
||||||
|
|
||||||
|
/* Set databits, stopbits, parity. */
|
||||||
|
extern int by_serial_set_parity(by_serial_t *serial_port, int databits, int stopbits, char parity);
|
||||||
|
|
||||||
|
/* Write buff of specified length, return wrote length. */
|
||||||
|
extern int by_serial_write(by_serial_t *serial_port, const char *buff, const int len);
|
||||||
|
|
||||||
|
/* Read buff of specified length, return read length. */
|
||||||
|
extern int by_serial_read(by_serial_t *serial_port, char *buff, const int len);
|
||||||
|
|
||||||
|
/* Return used serial buffer len. */
|
||||||
|
extern int by_serial_get_used_buffer_len(by_serial_t *serial_port);
|
||||||
|
|
||||||
|
#endif
|
||||||
19
logc/LICENSE
Normal file
19
logc/LICENSE
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2020 rxi
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
83
logc/README.md
Normal file
83
logc/README.md
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# log.c
|
||||||
|
A simple logging library implemented in C99
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
**[log.c](src/log.c?raw=1)** and **[log.h](src/log.h?raw=1)** should be dropped
|
||||||
|
into an existing project and compiled along with it. The library provides 6
|
||||||
|
function-like macros for logging:
|
||||||
|
|
||||||
|
```c
|
||||||
|
log_trace(const char *fmt, ...);
|
||||||
|
log_debug(const char *fmt, ...);
|
||||||
|
log_info(const char *fmt, ...);
|
||||||
|
log_warn(const char *fmt, ...);
|
||||||
|
log_error(const char *fmt, ...);
|
||||||
|
log_fatal(const char *fmt, ...);
|
||||||
|
```
|
||||||
|
|
||||||
|
Each function takes a printf format string followed by additional arguments:
|
||||||
|
|
||||||
|
```c
|
||||||
|
log_trace("Hello %s", "world")
|
||||||
|
```
|
||||||
|
|
||||||
|
Resulting in a line with the given format printed to stderr:
|
||||||
|
|
||||||
|
```
|
||||||
|
20:18:26 TRACE src/main.c:11: Hello world
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### log_set_quiet(bool enable)
|
||||||
|
Quiet-mode can be enabled by passing `true` to the `log_set_quiet()` function.
|
||||||
|
While this mode is enabled the library will not output anything to `stderr`, but
|
||||||
|
will continue to write to files and callbacks if any are set.
|
||||||
|
|
||||||
|
|
||||||
|
#### log_set_level(int level)
|
||||||
|
The current logging level can be set by using the `log_set_level()` function.
|
||||||
|
All logs below the given level will not be written to `stderr`. By default the
|
||||||
|
level is `LOG_TRACE`, such that nothing is ignored.
|
||||||
|
|
||||||
|
|
||||||
|
#### log_add_fp(FILE *fp, int level)
|
||||||
|
One or more file pointers where the log will be written can be provided to the
|
||||||
|
library by using the `log_add_fp()` function. The data written to the file
|
||||||
|
output is of the following format:
|
||||||
|
|
||||||
|
```
|
||||||
|
2047-03-11 20:18:26 TRACE src/main.c:11: Hello world
|
||||||
|
```
|
||||||
|
|
||||||
|
Any messages below the given `level` are ignored. If the library failed to add a
|
||||||
|
file pointer a value less-than-zero is returned.
|
||||||
|
|
||||||
|
|
||||||
|
#### log_add_callback(log_LogFn fn, void *udata, int level)
|
||||||
|
One or more callback functions which are called with the log data can be
|
||||||
|
provided to the library by using the `log_add_callback()` function. A callback
|
||||||
|
function is passed a `log_Event` structure containing the `line` number,
|
||||||
|
`filename`, `fmt` string, `va` printf va\_list, `level` and the given `udata`.
|
||||||
|
|
||||||
|
|
||||||
|
#### log_set_lock(log_LockFn fn, void *udata)
|
||||||
|
If the log will be written to from multiple threads a lock function can be set.
|
||||||
|
The function is passed the boolean `true` if the lock should be acquired or
|
||||||
|
`false` if the lock should be released and the given `udata` value.
|
||||||
|
|
||||||
|
|
||||||
|
#### const char* log_level_string(int level)
|
||||||
|
Returns the name of the given log level as a string.
|
||||||
|
|
||||||
|
|
||||||
|
#### LOG_USE_COLOR
|
||||||
|
If the library is compiled with `-DLOG_USE_COLOR` ANSI color escape codes will
|
||||||
|
be used when printing.
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
This library is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the MIT license. See [LICENSE](LICENSE) for details.
|
||||||
168
logc/log.c
Normal file
168
logc/log.c
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 rxi
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
* deal in the Software without restriction, including without limitation the
|
||||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
* sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#define MAX_CALLBACKS 32
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
log_LogFn fn;
|
||||||
|
void *udata;
|
||||||
|
int level;
|
||||||
|
} Callback;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
void *udata;
|
||||||
|
log_LockFn lock;
|
||||||
|
int level;
|
||||||
|
bool quiet;
|
||||||
|
Callback callbacks[MAX_CALLBACKS];
|
||||||
|
} L;
|
||||||
|
|
||||||
|
|
||||||
|
static const char *level_strings[] = {
|
||||||
|
"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef LOG_USE_COLOR
|
||||||
|
static const char *level_colors[] = {
|
||||||
|
"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static void stdout_callback(log_Event *ev) {
|
||||||
|
char buf[16];
|
||||||
|
buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0';
|
||||||
|
#ifdef LOG_USE_COLOR
|
||||||
|
fprintf(
|
||||||
|
ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ",
|
||||||
|
buf, level_colors[ev->level], level_strings[ev->level],
|
||||||
|
ev->file, ev->line);
|
||||||
|
#else
|
||||||
|
fprintf(
|
||||||
|
ev->udata, "%s %-5s %s:%d: ",
|
||||||
|
buf, level_strings[ev->level], ev->file, ev->line);
|
||||||
|
#endif
|
||||||
|
vfprintf(ev->udata, ev->fmt, ev->ap);
|
||||||
|
fprintf(ev->udata, "\n");
|
||||||
|
fflush(ev->udata);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void file_callback(log_Event *ev) {
|
||||||
|
char buf[64];
|
||||||
|
buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0';
|
||||||
|
fprintf(
|
||||||
|
ev->udata, "%s %-5s %s:%d: ",
|
||||||
|
buf, level_strings[ev->level], ev->file, ev->line);
|
||||||
|
vfprintf(ev->udata, ev->fmt, ev->ap);
|
||||||
|
fprintf(ev->udata, "\n");
|
||||||
|
fflush(ev->udata);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void lock(void) {
|
||||||
|
if (L.lock) { L.lock(true, L.udata); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void unlock(void) {
|
||||||
|
if (L.lock) { L.lock(false, L.udata); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char* log_level_string(int level) {
|
||||||
|
return level_strings[level];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void log_set_lock(log_LockFn fn, void *udata) {
|
||||||
|
L.lock = fn;
|
||||||
|
L.udata = udata;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void log_set_level(int level) {
|
||||||
|
L.level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void log_set_quiet(bool enable) {
|
||||||
|
L.quiet = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int log_add_callback(log_LogFn fn, void *udata, int level) {
|
||||||
|
for (int i = 0; i < MAX_CALLBACKS; i++) {
|
||||||
|
if (!L.callbacks[i].fn) {
|
||||||
|
L.callbacks[i] = (Callback) { fn, udata, level };
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int log_add_fp(FILE *fp, int level) {
|
||||||
|
return log_add_callback(file_callback, fp, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void init_event(log_Event *ev, void *udata) {
|
||||||
|
if (!ev->time) {
|
||||||
|
time_t t = time(NULL);
|
||||||
|
ev->time = localtime(&t);
|
||||||
|
}
|
||||||
|
ev->udata = udata;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void log_log(int level, const char *file, int line, const char *fmt, ...) {
|
||||||
|
log_Event ev = {
|
||||||
|
.fmt = fmt,
|
||||||
|
.file = file,
|
||||||
|
.line = line,
|
||||||
|
.level = level,
|
||||||
|
};
|
||||||
|
|
||||||
|
lock();
|
||||||
|
|
||||||
|
if (!L.quiet && level >= L.level) {
|
||||||
|
init_event(&ev, stderr);
|
||||||
|
va_start(ev.ap, fmt);
|
||||||
|
stdout_callback(&ev);
|
||||||
|
va_end(ev.ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) {
|
||||||
|
Callback *cb = &L.callbacks[i];
|
||||||
|
if (level >= cb->level) {
|
||||||
|
init_event(&ev, cb->udata);
|
||||||
|
va_start(ev.ap, fmt);
|
||||||
|
cb->fn(&ev);
|
||||||
|
va_end(ev.ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
64
logc/log.h
Normal file
64
logc/log.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2020 rxi
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the MIT license. See `log.c` for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LOG_H
|
||||||
|
#define LOG_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define LOG_VERSION "0.1.0"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
va_list ap;
|
||||||
|
const char *fmt;
|
||||||
|
const char *file;
|
||||||
|
struct tm *time;
|
||||||
|
void *udata;
|
||||||
|
int line;
|
||||||
|
int level;
|
||||||
|
} log_Event;
|
||||||
|
|
||||||
|
typedef void (*log_LogFn)(log_Event *ev);
|
||||||
|
typedef void (*log_LockFn)(bool lock, void *udata);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LOG_TRACE,
|
||||||
|
LOG_DEBUG,
|
||||||
|
LOG_INFO,
|
||||||
|
LOG_WARN,
|
||||||
|
LOG_ERROR,
|
||||||
|
LOG_FATAL
|
||||||
|
};
|
||||||
|
|
||||||
|
#define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
|
||||||
|
const char *log_level_string(int level);
|
||||||
|
void log_set_lock(log_LockFn fn, void *udata);
|
||||||
|
void log_set_level(int level);
|
||||||
|
void log_set_quiet(bool enable);
|
||||||
|
int log_add_callback(log_LogFn fn, void *udata, int level);
|
||||||
|
int log_add_fp(FILE *fp, int level);
|
||||||
|
|
||||||
|
void log_log(int level, const char *file, int line, const char *fmt, ...);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user