diff options
| author | David Faulkner <[email protected]> | 2026-08-07 23:40:47 -0500 |
|---|---|---|
| committer | David Faulkner <[email protected]> | 2026-08-07 23:40:47 -0500 |
| commit | b3e9e62599532050fc776c5e8f076915b56c2235 (patch) | |
| tree | af252346106a61b18cc6fc6fdbd32e962d096f1c /basic_blocks.c | |
Import official C23 code examples for Modern C (Jens Gustedt, 2024)HEADupstream-importmain
- Add official C source files, Makefile, c23-fallback.h, and LICENSE
- Update README.md with study mirror notice
Diffstat (limited to 'basic_blocks.c')
| -rw-r--r-- | basic_blocks.c | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/basic_blocks.c b/basic_blocks.c new file mode 100644 index 0000000..e0fcb05 --- /dev/null +++ b/basic_blocks.c @@ -0,0 +1,203 @@ +#include "sighandler.h" +#include <ctype.h> +#include <stdio.h> + +/* + * Because the parser is implemented here, and doesn't take care of + * strings, character constants, or comments, we hide the { } with + * compile time constants. + */ +constexpr char left = '{'; +constexpr char right = '}'; + +/** + ** @brief Initial string to indent a line + **/ +constexpr char head[] = ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>| "; + +/** + ** @brief Skips any number of spaces, tabs, or newlines in @a s + **/ +static +char const* skipspace(char const s[static 1]) { + while (isspace(s[0])) { + ++s; + } + return s; +} + +/** + ** @brief Exceptional states of the parse algorithm + **/ +enum state { + execution = 0, //!< Normal execution + plusL, //!< Too many left braces + plusR, //!< Too many right braces + tooDeep, //!< Nesting too deep to handle + eofOut, //!< End of output + interrupted, //!< Interrupted by a signal +}; + +/** + ** @brief Outputs end of line and eats all subsequent spaces on input + **/ +static +char const* end_line(char const s[static 1], jmp_buf jmpTarget) { + if (putchar('\n') == EOF) longjmp(jmpTarget, eofOut); + return skipspace(s); +} + + + +/** + ** @brief Keeps track of the last signal that interrupted us + **/ +static sig_atomic_t volatile interrupt = 0; + +static +char const* descend(char const* act, + unsigned dp[restrict static 1], // Bad + size_t len, char buffer[static len], + jmp_buf jmpTarget) { + if (dp[0]+3 > sizeof head) longjmp(jmpTarget, tooDeep); + ++dp[0];/*@\label{lab:incr-rec}*/ + NEW_LINE: // Loops on output + while (!act || !act[0]) { // Loops for input + if (interrupt) longjmp(jmpTarget, interrupted); + act = skipspace(fgets(buffer, len, stdin)); + if (!act) { // End of stream + if (dp[0] != 1) longjmp(jmpTarget, plusL); + else goto ASCEND; + } + } + fputs(&head[sizeof head - (dp[0] + 2)], stdout); // Header + + for (; act && act[0]; ++act) { // Remainder of the line + switch (act[0]) { /*@\label{lab:switch-char}*/ + case left: // Descends on left brace + act = end_line(act+1, jmpTarget); + act = descend(act, dp, len, buffer, jmpTarget);/*@\label{lab:descend}*/ + act = end_line(act+1, jmpTarget); + goto NEW_LINE; + case right: // Returns on right brace + if (dp[0] == 1) longjmp(jmpTarget, plusR); + else goto ASCEND; + default: // Prints char and goes on + putchar(act[0]); + } + } + goto NEW_LINE; + ASCEND: + --dp[0];/*@\label{lab:decr-rec}*/ + return act; +} + +constexpr unsigned maxline = 256; + +void basic_blocks(void) { + char buffer[maxline]; + unsigned depth = 0; + char const* format = + "All matching %0.0d'%c' '%c' pairs have been closed correctly\n"; + jmp_buf jmpTarget; + switch (setjmp(jmpTarget)) { + case 0: + descend(nullptr, &depth, maxline, buffer, jmpTarget); + break; + case plusL: + format = + "Warning: %d '%c' have not been closed properly (expected '%c')\n"; + break; + case plusR: + format = + "Error: closing too many (%d) '%c' parenthesis with additional '%c'\n"; + break; + case tooDeep: + format = + "Error: nesting (%d) of '%c' '%c' constructs is too deep\n"; + break; + case eofOut: + format = + "Error: EOF for stdout at %d open '%c', expecting same amount of '%c'\n"; + break; + case interrupted: + format = + "Interrupted at level %d of '%c' '%c' nesting\n"; + break; + default:; + format = + "Error: unknown error within (%d) '%c' '%c' constructs\n"; + } + fflush(stdout); + fprintf(stderr, format, depth, left, right); + if (interrupt) { + SH_PRINT(stderr, interrupt, + "is somebody trying to kill us?"); + raise(interrupt); + } +} + +/** + ** @brief A minimal signal handler + ** + ** After updating the signal count, for most signals this + ** simply stores the signal value in "interrupt" and returns. + **/ +static void signal_handler(int sig) { + sh_count(sig); + switch (sig) { + case SIGTERM: quick_exit(EXIT_FAILURE); + case SIGABRT: _Exit(EXIT_FAILURE); +#ifdef SIGCONT + // continue normal operation + case SIGCONT: return; +#endif + default: + /* reset the handling to its default */ + signal(sig, SIG_DFL); + interrupt = sig; + return; + } +} + +// Will point to the command-line arguments +static char** lastOpen = nullptr; + +// Checks if we were in the middle of an operation +void doAtExit(void) { + if (lastOpen && lastOpen[0]) { + fprintf(stderr, "\n***********\nabnormal exit, last open file was %s\n", lastOpen[0]); + } +} + +int main(int argc, char* argv[argc+1]) { + // Ensures that stdout is line buffered + if (setvbuf(stdout, nullptr, _IOLBF, maxline + sizeof head + 2)) { + fputs("we could not establish line buffering for stdout, terminating.", stderr); + return EXIT_FAILURE; + } + + // Establishes exit handlers + atexit(doAtExit); + at_quick_exit(doAtExit); + + // Establishes signal handlers + for (unsigned i = 1; i < sh_known; ++i) + sh_enable(i, signal_handler); + + // If there are no command-line arguments, reads from stdin + lastOpen = argv; + if (argc < 2) goto RUN; + + // Runs basic_blocks for each command-line argument + for (++lastOpen; lastOpen[0]; ++lastOpen) { + if (!freopen(lastOpen[0], "r", stdin)) { + perror(lastOpen[0]); + return EXIT_FAILURE; + } + printf("++++++++++ %s +++++++++++\n", lastOpen[0]); + RUN: + basic_blocks(); + } + return EXIT_SUCCESS; +} |
