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_blocks2.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_blocks2.c')
| -rw-r--r-- | basic_blocks2.c | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/basic_blocks2.c b/basic_blocks2.c new file mode 100644 index 0000000..b47a227 --- /dev/null +++ b/basic_blocks2.c @@ -0,0 +1,233 @@ +#include "sighandler.h" +#include <ctype.h> +#include <setjmp.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.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 { + 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; + +/** + ** @brief Keep track of the depth at which an error was detected. + **/ +static unsigned volatile deepest = 0; + +static +char const* descend1(char const act[static 1], + unsigned depth, + size_t len, char buffer[restrict static len], + jmp_buf jmpTarget) { + if (depth+2 > sizeof head) longjmp(jmpTarget, tooDeep); + NEW_LINE: // loop on output + while (!act[0]) { // loop for input + if (interrupt) longjmp(jmpTarget, interrupted); + act = fgets(buffer, len, stdin); + if (!act) { // end of stream + deepest = depth; + longjmp(jmpTarget, plusL); + } + act = skipspace(act); + } + // act is never 0 from here on + fputs(&head[sizeof head - (depth + 3)], stdout); // header + for (; act[0]; ++act) { // remainder of the line + switch (act[0]) { + case left: // descend on left brace + act = end_line(act+1, jmpTarget); + act = descend1(act, depth+1, len, buffer, jmpTarget); + act = end_line(act+1, jmpTarget); + goto NEW_LINE; + case right: // return on right brace + return act; + default: // print char and go on + putchar(act[0]); + } + } + goto NEW_LINE; +} + +static +void descend0(size_t len, char buffer[static len], jmp_buf jmpTarget) { + char const* act = nullptr; + NEW_LINE: // loop on output + do { // loop for input + if (interrupt) longjmp(jmpTarget, interrupted); + act = fgets(buffer, len, stdin); + if (!act) return; + act = skipspace(act); + } while (!act[0]); + // act is never 0 from here on + fputs(&head[sizeof head - 3], stdout); // header + for (; act[0]; ++act) { // remainder of the line + switch (act[0]) { + case left: // descend on left brace + act = end_line(act+1, jmpTarget); + act = descend1(act, 1, len, buffer, jmpTarget); + act = end_line(act+1, jmpTarget); + goto NEW_LINE; + case right: // right brace is an error + longjmp(jmpTarget, plusR); + default: // print char and go on + if (putchar(act[0]) == EOF) longjmp(jmpTarget, eofOut); + } + } + goto NEW_LINE; +} + +enum { maxline = 256 }; + +void basic_blocks(void) { + char buffer[maxline]; + char const* format = + "All matching %0.0d'%c' '%c' pairs have been closed correctly\n"; + jmp_buf jmpTarget; + switch (setjmp(jmpTarget)) { + case 0: + deepest = 0; + descend0(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, deepest, 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; +} |
