Jelajahi Sumber

feat: aFunlang支持捕获中断

Windows系统从stdin读入数据时支持中断
SongZihuan 3 tahun lalu
induk
melakukan
30f73e794b

+ 3 - 1
include/core/parser.h

@@ -8,6 +8,8 @@
 #define SYNTACTIC_MAX_DEEP (1000)
 typedef struct af_Parser af_Parser;
 
+typedef bool ParserStdinInterruptFunc(void);
+
 #define STDIN_MAX_SIZE (1024)
 
 /* Parser 创建与释放 */
@@ -16,7 +18,7 @@ makeParser(DLC_SYMBOL(readerFunc) read_func, DLC_SYMBOL(destructReaderFunc) dest
 AFUN_CORE_EXPORT void freeParser(af_Parser *parser);
 AFUN_CORE_EXPORT af_Parser *makeParserByString(char *str, bool free_str);
 AFUN_CORE_EXPORT af_Parser *makeParserByFile(FilePath path);
-AFUN_CORE_EXPORT af_Parser *makeParserByStdin();
+AFUN_CORE_EXPORT af_Parser *makeParserByStdin(ParserStdinInterruptFunc *interrupt);
 
 /* Parser 相关操作 */
 AFUN_CORE_EXPORT af_Code *parserCode(FilePath file, af_Parser *parser);

+ 1 - 1
include/runtime/aFunlang.h

@@ -20,7 +20,7 @@ AFUN_LANG_EXPORT void destructAFunEnvironment(af_Environment *env);
 /* 源文件运行 */
 AFUN_LANG_EXPORT int runCodeFromString(char *code, char *string_name, int mode, af_Environment *env);
 AFUN_LANG_EXPORT int runCodeFromFileSource(FilePath file, bool save_afb, FilePath save_path, int mode, af_Environment *env);
-AFUN_LANG_EXPORT int runCodeFromStdin(char *name, af_Environment *env);
+AFUN_LANG_EXPORT int runCodeFromStdin(char *name, ParserStdinInterruptFunc *interrupt, af_Environment *env);
 AFUN_LANG_EXPORT int runCodeFromMemory(af_Code *code, int mode, af_Environment *env);
 AFUN_LANG_EXPORT int runCodeFromFileByte(FilePath file, int mode, af_Environment *env);
 AFUN_LANG_EXPORT int runCodeFromFile(FilePath file, bool save_afb, int mode, af_Environment *env);

+ 7 - 0
include/tool/stdio_.h

@@ -3,8 +3,12 @@
 #include <stdio.h>
 
 AFUN_TOOL_EXPORT int fgets_stdin(char **dest, int len);
+AFUN_TOOL_EXPORT bool checkStdin(void);
 
 #ifdef aFunWIN32_NO_CYGWIN
+AFUN_TOOL_EXPORT int fgetchar_stdin(void);
+AFUN_TOOL_EXPORT int fungec_stdin(int ch);
+
 AFUN_TOOL_EXPORT int fputs_stdout(char *str);
 AFUN_TOOL_EXPORT int fputs_stderr(char *str);
 
@@ -14,6 +18,9 @@ AFUN_TOOL_EXPORT size_t printf_stdout(size_t buf_len, char *format, ...);
 AFUN_TOOL_EXPORT size_t printf_stderr(size_t buf_len, char *format, ...);
 
 #else
+#define fgetchar_stdin() fgetc(stdin)
+#define fungec_stdin(ch) ungetc((ch), stdin)
+
 #define fputs_stdout(str) fputs((str), stdout)
 #define fputs_stderr(str) fputs((str), stderr)
 

+ 19 - 6
src/core/parser.c

@@ -148,6 +148,7 @@ af_Parser *makeParserByFile(FilePath path){
 
 struct readerDataStdin {
     bool no_first;
+    bool (*interrupt)(void);  // 中断函数
 
     char *data;
     size_t index;
@@ -157,21 +158,32 @@ struct readerDataStdin {
 static size_t readFuncStdin(struct readerDataStdin *data, char *dest, size_t len, bool *read_end) {
     if (data->index == data->len) {  // 读取内容
         if (data->no_first)
-            printf("\r.... ");
+            fputs("\r.... ", stdout);
         else
-            printf("\r>>>> ");
+            fputs("\r>>>> ", stdout);
+        fflush(stdout);
         data->no_first = true;
-
         free(data->data);
 
-        int ch = getc(stdin);
+#if aFunWIN32_NO_CYGWIN
+        while (!checkStdin()) {  // 无内容则一直循环等到
+            /* 检查信号中断 */
+            if (data->interrupt != NULL && data->interrupt()) {  // 设置了中断函数, 并且该函数返回0
+                printf("\rInterrupt\n");
+                *read_end = true;
+                return 0;
+            }
+        }
+#endif
+
+        int ch = fgetchar_stdin();
         if (ch == '\n' || ch == EOF) {
             /* 读取结束 */
             *read_end = true;
             return 0;
         }
 
-        ungetc(ch, stdin);
+        fungec_stdin(ch);
 
         if (fgets_stdin(&data->data, STDIN_MAX_SIZE) == 0) {
             writeErrorLog(aFunCoreLogger, "The stdin buf too large (> %d)", STDIN_MAX_SIZE);
@@ -194,13 +206,14 @@ static void destructStdin(struct readerDataStdin *data) {
     free(data->data);
 }
 
-af_Parser *makeParserByStdin(){
+af_Parser *makeParserByStdin(ParserStdinInterruptFunc *interrupt){
     if (ferror(stdin))
         clearerr(stdin);
 
     DLC_SYMBOL(readerFunc) read_func = MAKE_SYMBOL(readFuncStdin, readerFunc);
     DLC_SYMBOL(destructReaderFunc) destruct = MAKE_SYMBOL(destructStdin, destructReaderFunc);
     af_Parser *parser = makeParser(read_func, destruct, sizeof(struct readerDataStdin));
+    ((struct readerDataStdin *)parser->reader->data)->interrupt = interrupt;
     initParser(parser);
     FREE_SYMBOL(read_func);
     FREE_SYMBOL(destruct);

+ 13 - 2
src/main.c

@@ -3,6 +3,7 @@
 #include "aFun.h"
 #include "main_run.h"
 #include "main_build.h"
+#include "main_signal.h"
 
 ff_defArg(help, false)
                 ff_argRule('h', help, not, 'h')
@@ -36,6 +37,7 @@ static const char *name = NULL;
 static int mainHelp(ff_FFlags *ff);
 static void printVersion(void);
 static void printHelp(void);
+static bool stdin_interrupt(void);
 static int mainRun(ff_FFlags *ff);
 static int mainCL(ff_FFlags *ff);
 static int mainBuild(ff_FFlags *ff);
@@ -77,6 +79,8 @@ INIT_ERROR:
     aFunlangLogger->buf = &main_buf;
     writeDebugLog(aFunlangLogger, "aFunlang-exe init success");
 
+    signalInit();
+
     int exit_code = EXIT_SUCCESS;
     ff_FFlags *ff = ff_initFFlags(argc, argv, true, false, stderr, aFunlang_exe);
     if (ff == NULL)
@@ -159,6 +163,13 @@ out:
     return EXIT_SUCCESS;
 }
 
+static bool stdin_interrupt(void) {
+    bool re = getSignal();
+    if (re)
+        writeInfoLog(aFunlangLogger, "Signal Interrupt");
+    return re;
+}
+
 static int mainRun(ff_FFlags *ff) {
     int exit_code;
     char **argv = NULL;
@@ -170,7 +181,7 @@ static int mainRun(ff_FFlags *ff) {
         env = creatAFunEnvironment(0, NULL);
         printWelcomeInfo();
         do
-            exit_code = runCodeFromStdin("stdin", env);
+            exit_code = runCodeFromStdin("stdin", stdin_interrupt, env);
         while (isCoreExit(env) != 1);
     } else {
         env = creatAFunEnvironment(argc - 1, argv + 1);
@@ -268,7 +279,7 @@ static int mainCL(ff_FFlags *ff) {
     if (command_line && isCoreExit(env) != 1) {
         printWelcomeInfo();
         do
-            exit_code = runCodeFromStdin("stdin", env);
+            exit_code = runCodeFromStdin("stdin", stdin_interrupt, env);
         while (isCoreExit(env) != 1);
     }
 

+ 31 - 0
src/main_signal.c

@@ -0,0 +1,31 @@
+#include "aFun.h"
+#include "signal.h"
+#include "main.h"
+
+static volatile sig_atomic_t sig = 0;  // SIGINT (Windows & *nix)
+
+static void sigFunc(int signum);
+static void sigFunc(int signum) {
+    if (signum != SIGINT && signum != SIGTERM)
+        return;
+    sig = 1;
+}
+
+bool getSignal(void) {
+    int re = sig;
+    if (re == 1) {
+        sig = 0;
+        signal(SIGINT, sigFunc);
+        signal(SIGTERM, sigFunc);
+        return true;
+    }
+    return false;
+}
+
+void signalInit(void) {
+    writeDebugLog(aFunlangLogger, "aFunlang signal init");
+    sig = 0;
+    signal(SIGINT, sigFunc);
+    signal(SIGTERM, sigFunc);
+}
+

+ 8 - 0
src/main_signal.h

@@ -0,0 +1,8 @@
+#ifndef AFUN_MAIN_SIGNAL_H
+#define AFUN_MAIN_SIGNAL_H
+#include "main.h"
+
+bool getSignal(void);
+void signalInit(void);
+
+#endif //AFUN_MAIN_SIGNAL_H

+ 2 - 2
src/runtime/aFunlang.c

@@ -140,14 +140,14 @@ int runCodeFromFileSource(FilePath file, bool save_afb, FilePath save_path, int
  * 函数名: runCodeFromStdin
  * 目标: 运行stdin的程序 (源码形式)
  */
-int runCodeFromStdin(char *name, af_Environment *env){
+int runCodeFromStdin(char *name, ParserStdinInterruptFunc *interrupt, af_Environment *env){
     if (env == NULL || ferror(stdin) || feof(stdin) || !aFunInit_mark)  // ferror在feof前执行
         return -1;
 
     if (name == NULL)
         name = "sys-stdin.aun";
 
-    af_Parser *parser = makeParserByStdin();
+    af_Parser *parser = makeParserByStdin(interrupt);
     return runCode_(name, parser, 0, NULL, env);
 }
 

+ 2 - 0
src/tool/file.c

@@ -11,7 +11,9 @@
 #include <stdlib.h>
 
 #ifdef aFunWIN32_NO_CYGWIN
+#ifdef _MSC_VER
 #pragma warning(disable : 5105)  // 关闭 5105 的警告输出 (Windows.h中使用)
+#endif
 #include <Windows.h>
 #else
 #include <unistd.h>

+ 63 - 0
src/tool/stdio_.c

@@ -13,10 +13,13 @@
 #ifdef _MSC_VER
 #pragma warning(disable : 5105)  // 关闭 5105 的警告输出 (Windows.h中使用)
 #endif
+#include <conio.h>
 #include <Windows.h>
 // 获取CodePage, 并将内存中utf-8字符串转换为对应编码输出
 // cygwin环境下, 终端默认为uft-8
 
+static bool stdin_empty = true;  // stdin读取内容遇到 \n, 用于检测stdin是否为空
+
 static int convertMultiByte(char **dest, char *str, UINT from, UINT to) {
     if (str == NULL || dest == NULL)
         return 0;
@@ -47,9 +50,40 @@ int fgets_stdin(char **dest, int len) {
     UINT code_page = GetConsoleCP();
     if (fgets(wstr, len, stdin) != NULL)
         re = convertMultiByte(dest, wstr, code_page, CP_UTF8);
+    if (strchr(wstr, '\n') != NULL)
+        stdin_empty = true;  // 没有读取到\n说明stdin还有内容
+    else
+        stdin_empty = false;  // 没有读取到\n说明stdin还有内容
     return re;
 }
 
+int fgetchar_stdin(void) {
+    int ch = getc(stdin);
+    if (ch == '\n')
+        stdin_empty = true;
+    else
+        stdin_empty = false;
+    return ch;
+}
+
+int fungec_stdin(int ch) {
+    int re = ungetc(ch, stdin);
+    stdin_empty = false;
+    return re;
+}
+
+/*
+ * 函数名: checkStdin
+ * 目标: 检查stdin缓冲区是否有内容
+ * 有内容则返回true
+ * 无内容则返回false
+ */
+bool checkStdin(void) {
+    if (!stdin_empty)
+        return true;
+    return _kbhit();
+}
+
 static int fputs_std_(char *str, FILE *std) {
     UINT code_page = GetConsoleCP();
     char *wstr = NULL;
@@ -107,6 +141,10 @@ size_t printf_stderr(size_t buf_len, char *format, ...) {
 }
 
 #else
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+
 // 用于Linux平台的IO函数
 // 默认Linux平台均使用utf-8
 
@@ -116,5 +154,30 @@ int fgets_stdin(char **dest, int len) {
         return 0;
     return 1;
 }
+/*
+ * 函数名: checkStdin
+ * 目标: 检查stdin缓冲区是否有内容
+ * 有内容则返回true
+ * 无内容则返回false
+ */
+bool checkStdin(void) {
+    struct termios oldt, newt;
+    int ch;
+    int oldf;
+    tcgetattr(STDIN_FILENO, &oldt);
+    newt = oldt;
+    newt.c_lflag &= ~(ICANON | ECHO);
+    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+    oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
+    fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
+    ch = getchar();
+    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
+    fcntl(STDIN_FILENO, F_SETFL, oldf);
+    if(ch != EOF) {
+        ungetc(ch, stdin);
+        return 1;
+    }
+    return 0;
+}
 
 #endif