├── .gitignore ├── LICENSE ├── README.md ├── clock.c ├── nob.c ├── nob.h ├── qlock.c ├── quine.c └── stb_c_lexer.h /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | nob 3 | nob.old -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 Alexey Kutepov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qlock.c 2 | 3 | Toolset for reproducing Quine Clock in C: https://gist.github.com/rexim/f582098611b2be202051ba543e21da05 4 | 5 | Inspired by https://x.com/aemkei/status/1795762928399880680 6 | 7 | ## Quick Start 8 | 9 | ```console 10 | $ cc -o nob nob.c 11 | $ ./nob 12 | $ ./build/qlock 13 | ``` 14 | -------------------------------------------------------------------------------- /clock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define FONT_COUNT 11 6 | int font[FONT_COUNT] = {31599,19812,14479,31207,23524,29411,29679,30866,31727,31719,1040}; 7 | #define FONT_ROWS 5 8 | #define FONT_COLS 3 9 | #define DIGITS_COUNT 8 10 | #define DIGITS_PAD 2 11 | #define DISPLAY_WIDTH (FONT_COLS + DIGITS_PAD)*DIGITS_COUNT 12 | #define DISPLAY_HEIGHT FONT_ROWS 13 | 14 | int main(void) 15 | { 16 | for (;;) { 17 | time_t t = time(NULL); 18 | struct tm *tm = localtime(&t); 19 | 20 | int digits[DIGITS_COUNT]; 21 | digits[0] = tm->tm_hour/10; 22 | digits[1] = tm->tm_hour%10; 23 | digits[2] = 10; 24 | digits[3] = tm->tm_min/10; 25 | digits[4] = tm->tm_min%10; 26 | digits[5] = 10; 27 | digits[6] = tm->tm_sec/10; 28 | digits[7] = tm->tm_sec%10; 29 | for (int y = 0; y < DISPLAY_HEIGHT; ++y) { 30 | for (int x = 0; x < DISPLAY_WIDTH; ++x) { 31 | int i = x/(FONT_COLS + DIGITS_PAD); 32 | int dx = x%(FONT_COLS + DIGITS_PAD); 33 | if (dx < FONT_COLS && (font[digits[i]]>>((FONT_ROWS - y - 1)*FONT_COLS + dx))&1) { 34 | // printf("\033[1;33m#\033[0m"); 35 | printf("\033[1;31m█\033[0m"); 36 | } else { 37 | printf("█"); 38 | } 39 | } 40 | printf("\n"); 41 | } 42 | 43 | printf("\033[%dA\033[%dD", DISPLAY_HEIGHT, DISPLAY_WIDTH); 44 | sleep(1); 45 | } 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /nob.c: -------------------------------------------------------------------------------- 1 | #define NOB_IMPLEMENTATION 2 | #include "nob.h" 3 | #define STB_C_LEXER_IMPLEMENTATION 4 | #include "stb_c_lexer.h" 5 | 6 | bool build_exe(Nob_Cmd *cmd, const char *output_path, const char *input_path) 7 | { 8 | cmd->count = 0; 9 | nob_cmd_append(cmd, "cc", "-Wall", "-Wextra"); 10 | nob_cmd_append(cmd, "-o", output_path); 11 | nob_cmd_append(cmd, "-include", "time.h"); 12 | nob_cmd_append(cmd, input_path); 13 | return nob_cmd_run_sync(*cmd); 14 | } 15 | 16 | bool compile_quine_blob(const char *output_path, const char *input_path) 17 | { 18 | Nob_String_Builder sb = {0}; 19 | if (!nob_read_entire_file(input_path, &sb)) return false; 20 | 21 | FILE *output = fopen(output_path, "wb"); 22 | if (output == NULL) { 23 | nob_log(NOB_ERROR, "Could not read file %s: %s", output_path, strerror(errno)); 24 | return false; 25 | } 26 | 27 | for (size_t i = 0; i < sb.count; ++i) { 28 | if (sb.items[i] == '?') { 29 | for (size_t j = 0; j < sb.count; ++j) { 30 | switch (sb.items[j]) { 31 | case '\n': fprintf(output, "\\n\"\n\""); break; 32 | case '\\': fprintf(output, "\\\\"); break; 33 | case '"': fprintf(output, "\\\""); break; 34 | default: fprintf(output, "%c", sb.items[j]); 35 | } 36 | } 37 | } else { 38 | fprintf(output, "%c", sb.items[i]); 39 | } 40 | } 41 | 42 | fclose(output); 43 | 44 | nob_log(NOB_INFO, "Generated %s", output_path); 45 | 46 | return true; 47 | } 48 | 49 | bool is_unconcatable(long token) 50 | { 51 | return (token == CLEX_id || token == CLEX_intlit); 52 | } 53 | 54 | bool format_tokens(const char *output_path, const char *input_path) 55 | { 56 | Nob_String_Builder sb = {0}; 57 | if (!nob_read_entire_file(input_path, &sb)) return false; 58 | 59 | FILE *output = fopen(output_path, "wb"); 60 | if (output == NULL) { 61 | nob_log(NOB_ERROR, "Could not read file %s: %s", output_path, strerror(errno)); 62 | return false; 63 | } 64 | 65 | stb_lexer l = {0}; 66 | static char string_store[1024]; 67 | stb_c_lexer_init(&l, sb.items, sb.items + sb.count, string_store, NOB_ARRAY_LEN(string_store)); 68 | 69 | int x = 8; 70 | int prev_token = 0; 71 | while (stb_c_lexer_get_token(&l)) { 72 | int n = l.where_lastchar - l.where_firstchar + 1; 73 | if (is_unconcatable(prev_token) && is_unconcatable(l.token)) { 74 | fprintf(output, " "); 75 | x += 1; 76 | } 77 | prev_token = l.token; 78 | fprintf(output, "%.*s", n, l.where_firstchar); 79 | x += n; 80 | if (x >= 80) { 81 | fprintf(output, "\n"); 82 | x = 0; 83 | } 84 | } 85 | fclose(output); 86 | nob_log(NOB_INFO, "Generated %s", output_path); 87 | return true; 88 | } 89 | 90 | int main(int argc, char **argv) 91 | { 92 | NOB_GO_REBUILD_URSELF(argc, argv); 93 | 94 | if (!nob_mkdir_if_not_exists("./build/")) return 1; 95 | 96 | Nob_Cmd cmd = {0}; 97 | if (!build_exe(&cmd, "./build/quine", "quine.c")) return 1; 98 | if (!build_exe(&cmd, "./build/clock", "clock.c")) return 1; 99 | if (!format_tokens("./build/qlock-formatted.c", "qlock.c")) return 1; 100 | if (!compile_quine_blob("./build/qlock-blob.c", "./build/qlock-formatted.c")) return 1; 101 | if (!build_exe(&cmd, "./build/qlock", "./build/qlock-blob.c")) return 1; 102 | 103 | return 0; 104 | } 105 | -------------------------------------------------------------------------------- /nob.h: -------------------------------------------------------------------------------- 1 | // This is a complete backward incompatible rewrite of https://github.com/tsoding/nobuild 2 | // because I'm really unhappy with the direction it is going. It's gonna sit in this repo 3 | // until it's matured enough and then I'll probably extract it to its own repo. 4 | 5 | // Copyright 2023 Alexey Kutepov 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | #ifndef NOB_H_ 27 | #define NOB_H_ 28 | 29 | #define NOB_ASSERT assert 30 | #define NOB_REALLOC realloc 31 | #define NOB_FREE free 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #ifdef _WIN32 43 | # define WIN32_LEAN_AND_MEAN 44 | # define _WINUSER_ 45 | # define _WINGDI_ 46 | # define _IMM_ 47 | # define _WINCON_ 48 | # include 49 | # include 50 | # include 51 | #else 52 | # include 53 | # include 54 | # include 55 | # include 56 | # include 57 | #endif 58 | 59 | #ifdef _WIN32 60 | # define NOB_LINE_END "\r\n" 61 | #else 62 | # define NOB_LINE_END "\n" 63 | #endif 64 | 65 | #define NOB_ARRAY_LEN(array) (sizeof(array)/sizeof(array[0])) 66 | #define NOB_ARRAY_GET(array, index) \ 67 | (NOB_ASSERT((size_t)index < NOB_ARRAY_LEN(array)), array[(size_t)index]) 68 | 69 | typedef enum { 70 | NOB_INFO, 71 | NOB_WARNING, 72 | NOB_ERROR, 73 | } Nob_Log_Level; 74 | 75 | void nob_log(Nob_Log_Level level, const char *fmt, ...); 76 | 77 | // It is an equivalent of shift command from bash. It basically pops a command line 78 | // argument from the beginning. 79 | char *nob_shift_args(int *argc, char ***argv); 80 | 81 | typedef struct { 82 | const char **items; 83 | size_t count; 84 | size_t capacity; 85 | } Nob_File_Paths; 86 | 87 | typedef enum { 88 | NOB_FILE_REGULAR = 0, 89 | NOB_FILE_DIRECTORY, 90 | NOB_FILE_SYMLINK, 91 | NOB_FILE_OTHER, 92 | } Nob_File_Type; 93 | 94 | bool nob_mkdir_if_not_exists(const char *path); 95 | bool nob_copy_file(const char *src_path, const char *dst_path); 96 | bool nob_copy_directory_recursively(const char *src_path, const char *dst_path); 97 | bool nob_read_entire_dir(const char *parent, Nob_File_Paths *children); 98 | bool nob_write_entire_file(const char *path, const void *data, size_t size); 99 | Nob_File_Type nob_get_file_type(const char *path); 100 | 101 | #define nob_return_defer(value) do { result = (value); goto defer; } while(0) 102 | 103 | // Initial capacity of a dynamic array 104 | #define NOB_DA_INIT_CAP 256 105 | 106 | // Append an item to a dynamic array 107 | #define nob_da_append(da, item) \ 108 | do { \ 109 | if ((da)->count >= (da)->capacity) { \ 110 | (da)->capacity = (da)->capacity == 0 ? NOB_DA_INIT_CAP : (da)->capacity*2; \ 111 | (da)->items = NOB_REALLOC((da)->items, (da)->capacity*sizeof(*(da)->items)); \ 112 | NOB_ASSERT((da)->items != NULL && "Buy more RAM lol"); \ 113 | } \ 114 | \ 115 | (da)->items[(da)->count++] = (item); \ 116 | } while (0) 117 | 118 | #define nob_da_free(da) NOB_FREE((da).items) 119 | 120 | // Append several items to a dynamic array 121 | #define nob_da_append_many(da, new_items, new_items_count) \ 122 | do { \ 123 | if ((da)->count + (new_items_count) > (da)->capacity) { \ 124 | if ((da)->capacity == 0) { \ 125 | (da)->capacity = NOB_DA_INIT_CAP; \ 126 | } \ 127 | while ((da)->count + (new_items_count) > (da)->capacity) { \ 128 | (da)->capacity *= 2; \ 129 | } \ 130 | (da)->items = NOB_REALLOC((da)->items, (da)->capacity*sizeof(*(da)->items)); \ 131 | NOB_ASSERT((da)->items != NULL && "Buy more RAM lol"); \ 132 | } \ 133 | memcpy((da)->items + (da)->count, (new_items), (new_items_count)*sizeof(*(da)->items)); \ 134 | (da)->count += (new_items_count); \ 135 | } while (0) 136 | 137 | typedef struct { 138 | char *items; 139 | size_t count; 140 | size_t capacity; 141 | } Nob_String_Builder; 142 | 143 | bool nob_read_entire_file(const char *path, Nob_String_Builder *sb); 144 | 145 | // Append a sized buffer to a string builder 146 | #define nob_sb_append_buf(sb, buf, size) nob_da_append_many(sb, buf, size) 147 | 148 | // Append a NULL-terminated string to a string builder 149 | #define nob_sb_append_cstr(sb, cstr) \ 150 | do { \ 151 | const char *s = (cstr); \ 152 | size_t n = strlen(s); \ 153 | nob_da_append_many(sb, s, n); \ 154 | } while (0) 155 | 156 | // Append a single NULL character at the end of a string builder. So then you can 157 | // use it a NULL-terminated C string 158 | #define nob_sb_append_null(sb) nob_da_append_many(sb, "", 1) 159 | 160 | // Free the memory allocated by a string builder 161 | #define nob_sb_free(sb) NOB_FREE((sb).items) 162 | 163 | // Process handle 164 | #ifdef _WIN32 165 | typedef HANDLE Nob_Proc; 166 | #define NOB_INVALID_PROC INVALID_HANDLE_VALUE 167 | #else 168 | typedef int Nob_Proc; 169 | #define NOB_INVALID_PROC (-1) 170 | #endif // _WIN32 171 | 172 | typedef struct { 173 | Nob_Proc *items; 174 | size_t count; 175 | size_t capacity; 176 | } Nob_Procs; 177 | 178 | bool nob_procs_wait(Nob_Procs procs); 179 | 180 | // Wait until the process has finished 181 | bool nob_proc_wait(Nob_Proc proc); 182 | 183 | // A command - the main workhorse of Nob. Nob is all about building commands an running them 184 | typedef struct { 185 | const char **items; 186 | size_t count; 187 | size_t capacity; 188 | } Nob_Cmd; 189 | 190 | // Render a string representation of a command into a string builder. Keep in mind the the 191 | // string builder is not NULL-terminated by default. Use nob_sb_append_null if you plan to 192 | // use it as a C string. 193 | void nob_cmd_render(Nob_Cmd cmd, Nob_String_Builder *render); 194 | 195 | #define nob_cmd_append(cmd, ...) \ 196 | nob_da_append_many(cmd, \ 197 | ((const char*[]){__VA_ARGS__}), \ 198 | (sizeof((const char*[]){__VA_ARGS__})/sizeof(const char*))) 199 | 200 | // Free all the memory allocated by command arguments 201 | #define nob_cmd_free(cmd) NOB_FREE(cmd.items) 202 | 203 | // Run command asynchronously 204 | Nob_Proc nob_cmd_run_async(Nob_Cmd cmd); 205 | 206 | // Run command synchronously 207 | bool nob_cmd_run_sync(Nob_Cmd cmd); 208 | 209 | #ifndef NOB_TEMP_CAPACITY 210 | #define NOB_TEMP_CAPACITY (8*1024*1024) 211 | #endif // NOB_TEMP_CAPACITY 212 | char *nob_temp_strdup(const char *cstr); 213 | void *nob_temp_alloc(size_t size); 214 | char *nob_temp_sprintf(const char *format, ...); 215 | void nob_temp_reset(void); 216 | size_t nob_temp_save(void); 217 | void nob_temp_rewind(size_t checkpoint); 218 | 219 | int is_path1_modified_after_path2(const char *path1, const char *path2); 220 | bool nob_rename(const char *old_path, const char *new_path); 221 | int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t input_paths_count); 222 | int nob_needs_rebuild1(const char *output_path, const char *input_path); 223 | int nob_file_exists(const char *file_path); 224 | 225 | // TODO: add MinGW support for Go Rebuild Urself™ Technology 226 | #ifndef NOB_REBUILD_URSELF 227 | # if _WIN32 228 | # if defined(__GNUC__) 229 | # define NOB_REBUILD_URSELF(binary_path, source_path) "gcc", "-o", binary_path, source_path 230 | # elif defined(__clang__) 231 | # define NOB_REBUILD_URSELF(binary_path, source_path) "clang", "-o", binary_path, source_path 232 | # elif defined(_MSC_VER) 233 | # define NOB_REBUILD_URSELF(binary_path, source_path) "cl.exe", nob_temp_sprintf("/Fe:%s", (binary_path)), source_path 234 | # endif 235 | # else 236 | # define NOB_REBUILD_URSELF(binary_path, source_path) "cc", "-o", binary_path, source_path 237 | # endif 238 | #endif 239 | 240 | // Go Rebuild Urself™ Technology 241 | // 242 | // How to use it: 243 | // int main(int argc, char** argv) { 244 | // GO_REBUILD_URSELF(argc, argv); 245 | // // actual work 246 | // return 0; 247 | // } 248 | // 249 | // After your added this macro every time you run ./nobuild it will detect 250 | // that you modified its original source code and will try to rebuild itself 251 | // before doing any actual work. So you only need to bootstrap your build system 252 | // once. 253 | // 254 | // The modification is detected by comparing the last modified times of the executable 255 | // and its source code. The same way the make utility usually does it. 256 | // 257 | // The rebuilding is done by using the REBUILD_URSELF macro which you can redefine 258 | // if you need a special way of bootstraping your build system. (which I personally 259 | // do not recommend since the whole idea of nobuild is to keep the process of bootstrapping 260 | // as simple as possible and doing all of the actual work inside of the nobuild) 261 | // 262 | #define NOB_GO_REBUILD_URSELF(argc, argv) \ 263 | do { \ 264 | const char *source_path = __FILE__; \ 265 | assert(argc >= 1); \ 266 | const char *binary_path = argv[0]; \ 267 | \ 268 | int rebuild_is_needed = nob_needs_rebuild(binary_path, &source_path, 1); \ 269 | if (rebuild_is_needed < 0) exit(1); \ 270 | if (rebuild_is_needed) { \ 271 | Nob_String_Builder sb = {0}; \ 272 | nob_sb_append_cstr(&sb, binary_path); \ 273 | nob_sb_append_cstr(&sb, ".old"); \ 274 | nob_sb_append_null(&sb); \ 275 | \ 276 | if (!nob_rename(binary_path, sb.items)) exit(1); \ 277 | Nob_Cmd rebuild = {0}; \ 278 | nob_cmd_append(&rebuild, NOB_REBUILD_URSELF(binary_path, source_path)); \ 279 | bool rebuild_succeeded = nob_cmd_run_sync(rebuild); \ 280 | nob_cmd_free(rebuild); \ 281 | if (!rebuild_succeeded) { \ 282 | nob_rename(sb.items, binary_path); \ 283 | exit(1); \ 284 | } \ 285 | \ 286 | Nob_Cmd cmd = {0}; \ 287 | nob_da_append_many(&cmd, argv, argc); \ 288 | if (!nob_cmd_run_sync(cmd)) exit(1); \ 289 | exit(0); \ 290 | } \ 291 | } while(0) 292 | // The implementation idea is stolen from https://github.com/zhiayang/nabs 293 | 294 | typedef struct { 295 | size_t count; 296 | const char *data; 297 | } Nob_String_View; 298 | 299 | const char *nob_temp_sv_to_cstr(Nob_String_View sv); 300 | 301 | Nob_String_View nob_sv_chop_by_delim(Nob_String_View *sv, char delim); 302 | Nob_String_View nob_sv_trim(Nob_String_View sv); 303 | bool nob_sv_eq(Nob_String_View a, Nob_String_View b); 304 | Nob_String_View nob_sv_from_cstr(const char *cstr); 305 | Nob_String_View nob_sv_from_parts(const char *data, size_t count); 306 | 307 | // printf macros for String_View 308 | #ifndef SV_Fmt 309 | #define SV_Fmt "%.*s" 310 | #endif // SV_Fmt 311 | #ifndef SV_Arg 312 | #define SV_Arg(sv) (int) (sv).count, (sv).data 313 | #endif // SV_Arg 314 | // USAGE: 315 | // String_View name = ...; 316 | // printf("Name: "SV_Fmt"\n", SV_Arg(name)); 317 | 318 | 319 | // minirent.h HEADER BEGIN //////////////////////////////////////// 320 | // Copyright 2021 Alexey Kutepov 321 | // 322 | // Permission is hereby granted, free of charge, to any person obtaining 323 | // a copy of this software and associated documentation files (the 324 | // "Software"), to deal in the Software without restriction, including 325 | // without limitation the rights to use, copy, modify, merge, publish, 326 | // distribute, sublicense, and/or sell copies of the Software, and to 327 | // permit persons to whom the Software is furnished to do so, subject to 328 | // the following conditions: 329 | // 330 | // The above copyright notice and this permission notice shall be 331 | // included in all copies or substantial portions of the Software. 332 | // 333 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 334 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 335 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 336 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 337 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 338 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 339 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 340 | // 341 | // ============================================================ 342 | // 343 | // minirent — 0.0.1 — A subset of dirent interface for Windows. 344 | // 345 | // https://github.com/tsoding/minirent 346 | // 347 | // ============================================================ 348 | // 349 | // ChangeLog (https://semver.org/ is implied) 350 | // 351 | // 0.0.2 Automatically include dirent.h on non-Windows 352 | // platforms 353 | // 0.0.1 First Official Release 354 | 355 | #ifndef _WIN32 356 | #include 357 | #else // _WIN32 358 | 359 | #define WIN32_LEAN_AND_MEAN 360 | #include "windows.h" 361 | 362 | struct dirent 363 | { 364 | char d_name[MAX_PATH+1]; 365 | }; 366 | 367 | typedef struct DIR DIR; 368 | 369 | static DIR *opendir(const char *dirpath); 370 | static struct dirent *readdir(DIR *dirp); 371 | static int closedir(DIR *dirp); 372 | #endif // _WIN32 373 | // minirent.h HEADER END //////////////////////////////////////// 374 | 375 | #endif // NOB_H_ 376 | 377 | #ifdef NOB_IMPLEMENTATION 378 | 379 | static size_t nob_temp_size = 0; 380 | static char nob_temp[NOB_TEMP_CAPACITY] = {0}; 381 | 382 | bool nob_mkdir_if_not_exists(const char *path) 383 | { 384 | #ifdef _WIN32 385 | int result = mkdir(path); 386 | #else 387 | int result = mkdir(path, 0755); 388 | #endif 389 | if (result < 0) { 390 | if (errno == EEXIST) { 391 | nob_log(NOB_INFO, "directory `%s` already exists", path); 392 | return true; 393 | } 394 | nob_log(NOB_ERROR, "could not create directory `%s`: %s", path, strerror(errno)); 395 | return false; 396 | } 397 | 398 | nob_log(NOB_INFO, "created directory `%s`", path); 399 | return true; 400 | } 401 | 402 | bool nob_copy_file(const char *src_path, const char *dst_path) 403 | { 404 | nob_log(NOB_INFO, "copying %s -> %s", src_path, dst_path); 405 | #ifdef _WIN32 406 | if (!CopyFile(src_path, dst_path, FALSE)) { 407 | nob_log(NOB_ERROR, "Could not copy file: %lu", GetLastError()); 408 | return false; 409 | } 410 | return true; 411 | #else 412 | int src_fd = -1; 413 | int dst_fd = -1; 414 | size_t buf_size = 32*1024; 415 | char *buf = NOB_REALLOC(NULL, buf_size); 416 | NOB_ASSERT(buf != NULL && "Buy more RAM lol!!"); 417 | bool result = true; 418 | 419 | src_fd = open(src_path, O_RDONLY); 420 | if (src_fd < 0) { 421 | nob_log(NOB_ERROR, "Could not open file %s: %s", src_path, strerror(errno)); 422 | nob_return_defer(false); 423 | } 424 | 425 | struct stat src_stat; 426 | if (fstat(src_fd, &src_stat) < 0) { 427 | nob_log(NOB_ERROR, "Could not get mode of file %s: %s", src_path, strerror(errno)); 428 | nob_return_defer(false); 429 | } 430 | 431 | dst_fd = open(dst_path, O_CREAT | O_TRUNC | O_WRONLY, src_stat.st_mode); 432 | if (dst_fd < 0) { 433 | nob_log(NOB_ERROR, "Could not create file %s: %s", dst_path, strerror(errno)); 434 | nob_return_defer(false); 435 | } 436 | 437 | for (;;) { 438 | ssize_t n = read(src_fd, buf, buf_size); 439 | if (n == 0) break; 440 | if (n < 0) { 441 | nob_log(NOB_ERROR, "Could not read from file %s: %s", src_path, strerror(errno)); 442 | nob_return_defer(false); 443 | } 444 | char *buf2 = buf; 445 | while (n > 0) { 446 | ssize_t m = write(dst_fd, buf2, n); 447 | if (m < 0) { 448 | nob_log(NOB_ERROR, "Could not write to file %s: %s", dst_path, strerror(errno)); 449 | nob_return_defer(false); 450 | } 451 | n -= m; 452 | buf2 += m; 453 | } 454 | } 455 | 456 | defer: 457 | free(buf); 458 | close(src_fd); 459 | close(dst_fd); 460 | return result; 461 | #endif 462 | } 463 | 464 | void nob_cmd_render(Nob_Cmd cmd, Nob_String_Builder *render) 465 | { 466 | for (size_t i = 0; i < cmd.count; ++i) { 467 | const char *arg = cmd.items[i]; 468 | if (arg == NULL) break; 469 | if (i > 0) nob_sb_append_cstr(render, " "); 470 | if (!strchr(arg, ' ')) { 471 | nob_sb_append_cstr(render, arg); 472 | } else { 473 | nob_da_append(render, '\''); 474 | nob_sb_append_cstr(render, arg); 475 | nob_da_append(render, '\''); 476 | } 477 | } 478 | } 479 | 480 | Nob_Proc nob_cmd_run_async(Nob_Cmd cmd) 481 | { 482 | if (cmd.count < 1) { 483 | nob_log(NOB_ERROR, "Could not run empty command"); 484 | return NOB_INVALID_PROC; 485 | } 486 | 487 | Nob_String_Builder sb = {0}; 488 | nob_cmd_render(cmd, &sb); 489 | nob_sb_append_null(&sb); 490 | nob_log(NOB_INFO, "CMD: %s", sb.items); 491 | nob_sb_free(sb); 492 | memset(&sb, 0, sizeof(sb)); 493 | 494 | #ifdef _WIN32 495 | // https://docs.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output 496 | 497 | STARTUPINFO siStartInfo; 498 | ZeroMemory(&siStartInfo, sizeof(siStartInfo)); 499 | siStartInfo.cb = sizeof(STARTUPINFO); 500 | // NOTE: theoretically setting NULL to std handles should not be a problem 501 | // https://docs.microsoft.com/en-us/windows/console/getstdhandle?redirectedfrom=MSDN#attachdetach-behavior 502 | // TODO: check for errors in GetStdHandle 503 | siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); 504 | siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); 505 | siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); 506 | siStartInfo.dwFlags |= STARTF_USESTDHANDLES; 507 | 508 | PROCESS_INFORMATION piProcInfo; 509 | ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); 510 | 511 | // TODO: use a more reliable rendering of the command instead of cmd_render 512 | // cmd_render is for logging primarily 513 | nob_cmd_render(cmd, &sb); 514 | nob_sb_append_null(&sb); 515 | BOOL bSuccess = CreateProcessA(NULL, sb.items, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo); 516 | nob_sb_free(sb); 517 | 518 | if (!bSuccess) { 519 | nob_log(NOB_ERROR, "Could not create child process: %lu", GetLastError()); 520 | return NOB_INVALID_PROC; 521 | } 522 | 523 | CloseHandle(piProcInfo.hThread); 524 | 525 | return piProcInfo.hProcess; 526 | #else 527 | pid_t cpid = fork(); 528 | if (cpid < 0) { 529 | nob_log(NOB_ERROR, "Could not fork child process: %s", strerror(errno)); 530 | return NOB_INVALID_PROC; 531 | } 532 | 533 | if (cpid == 0) { 534 | // NOTE: This leaks a bit of memory in the child process. 535 | // But do we actually care? It's a one off leak anyway... 536 | Nob_Cmd cmd_null = {0}; 537 | nob_da_append_many(&cmd_null, cmd.items, cmd.count); 538 | nob_cmd_append(&cmd_null, NULL); 539 | 540 | if (execvp(cmd.items[0], (char * const*) cmd_null.items) < 0) { 541 | nob_log(NOB_ERROR, "Could not exec child process: %s", strerror(errno)); 542 | exit(1); 543 | } 544 | NOB_ASSERT(0 && "unreachable"); 545 | } 546 | 547 | return cpid; 548 | #endif 549 | } 550 | 551 | bool nob_procs_wait(Nob_Procs procs) 552 | { 553 | bool success = true; 554 | for (size_t i = 0; i < procs.count; ++i) { 555 | success = nob_proc_wait(procs.items[i]) && success; 556 | } 557 | return success; 558 | } 559 | 560 | bool nob_proc_wait(Nob_Proc proc) 561 | { 562 | if (proc == NOB_INVALID_PROC) return false; 563 | 564 | #ifdef _WIN32 565 | DWORD result = WaitForSingleObject( 566 | proc, // HANDLE hHandle, 567 | INFINITE // DWORD dwMilliseconds 568 | ); 569 | 570 | if (result == WAIT_FAILED) { 571 | nob_log(NOB_ERROR, "could not wait on child process: %lu", GetLastError()); 572 | return false; 573 | } 574 | 575 | DWORD exit_status; 576 | if (!GetExitCodeProcess(proc, &exit_status)) { 577 | nob_log(NOB_ERROR, "could not get process exit code: %lu", GetLastError()); 578 | return false; 579 | } 580 | 581 | if (exit_status != 0) { 582 | nob_log(NOB_ERROR, "command exited with exit code %lu", exit_status); 583 | return false; 584 | } 585 | 586 | CloseHandle(proc); 587 | 588 | return true; 589 | #else 590 | for (;;) { 591 | int wstatus = 0; 592 | if (waitpid(proc, &wstatus, 0) < 0) { 593 | nob_log(NOB_ERROR, "could not wait on command (pid %d): %s", proc, strerror(errno)); 594 | return false; 595 | } 596 | 597 | if (WIFEXITED(wstatus)) { 598 | int exit_status = WEXITSTATUS(wstatus); 599 | if (exit_status != 0) { 600 | nob_log(NOB_ERROR, "command exited with exit code %d", exit_status); 601 | return false; 602 | } 603 | 604 | break; 605 | } 606 | 607 | if (WIFSIGNALED(wstatus)) { 608 | nob_log(NOB_ERROR, "command process was terminated by %s", strsignal(WTERMSIG(wstatus))); 609 | return false; 610 | } 611 | } 612 | 613 | return true; 614 | #endif 615 | } 616 | 617 | bool nob_cmd_run_sync(Nob_Cmd cmd) 618 | { 619 | Nob_Proc p = nob_cmd_run_async(cmd); 620 | if (p == NOB_INVALID_PROC) return false; 621 | return nob_proc_wait(p); 622 | } 623 | 624 | char *nob_shift_args(int *argc, char ***argv) 625 | { 626 | NOB_ASSERT(*argc > 0); 627 | char *result = **argv; 628 | (*argv) += 1; 629 | (*argc) -= 1; 630 | return result; 631 | } 632 | 633 | void nob_log(Nob_Log_Level level, const char *fmt, ...) 634 | { 635 | switch (level) { 636 | case NOB_INFO: 637 | fprintf(stderr, "[INFO] "); 638 | break; 639 | case NOB_WARNING: 640 | fprintf(stderr, "[WARNING] "); 641 | break; 642 | case NOB_ERROR: 643 | fprintf(stderr, "[ERROR] "); 644 | break; 645 | default: 646 | NOB_ASSERT(0 && "unreachable"); 647 | } 648 | 649 | va_list args; 650 | va_start(args, fmt); 651 | vfprintf(stderr, fmt, args); 652 | va_end(args); 653 | fprintf(stderr, "\n"); 654 | } 655 | 656 | bool nob_read_entire_dir(const char *parent, Nob_File_Paths *children) 657 | { 658 | bool result = true; 659 | DIR *dir = NULL; 660 | 661 | dir = opendir(parent); 662 | if (dir == NULL) { 663 | nob_log(NOB_ERROR, "Could not open directory %s: %s", parent, strerror(errno)); 664 | nob_return_defer(false); 665 | } 666 | 667 | errno = 0; 668 | struct dirent *ent = readdir(dir); 669 | while (ent != NULL) { 670 | nob_da_append(children, nob_temp_strdup(ent->d_name)); 671 | ent = readdir(dir); 672 | } 673 | 674 | if (errno != 0) { 675 | nob_log(NOB_ERROR, "Could not read directory %s: %s", parent, strerror(errno)); 676 | nob_return_defer(false); 677 | } 678 | 679 | defer: 680 | if (dir) closedir(dir); 681 | return result; 682 | } 683 | 684 | bool nob_write_entire_file(const char *path, const void *data, size_t size) 685 | { 686 | bool result = true; 687 | 688 | FILE *f = fopen(path, "wb"); 689 | if (f == NULL) { 690 | nob_log(NOB_ERROR, "Could not open file %s for writing: %s\n", path, strerror(errno)); 691 | nob_return_defer(false); 692 | } 693 | 694 | // len 695 | // v 696 | // aaaaaaaaaa 697 | // ^ 698 | // data 699 | 700 | const char *buf = data; 701 | while (size > 0) { 702 | size_t n = fwrite(buf, 1, size, f); 703 | if (ferror(f)) { 704 | nob_log(NOB_ERROR, "Could not write into file %s: %s\n", path, strerror(errno)); 705 | nob_return_defer(false); 706 | } 707 | size -= n; 708 | buf += n; 709 | } 710 | 711 | defer: 712 | if (f) fclose(f); 713 | return result; 714 | } 715 | 716 | Nob_File_Type nob_get_file_type(const char *path) 717 | { 718 | #ifdef _WIN32 719 | DWORD attr = GetFileAttributesA(path); 720 | if (attr == INVALID_FILE_ATTRIBUTES) { 721 | nob_log(NOB_ERROR, "Could not get file attributes of %s: %lu", path, GetLastError()); 722 | return -1; 723 | } 724 | 725 | if (attr & FILE_ATTRIBUTE_DIRECTORY) return NOB_FILE_DIRECTORY; 726 | // TODO: detect symlinks on Windows (whatever that means on Windows anyway) 727 | return NOB_FILE_REGULAR; 728 | #else // _WIN32 729 | struct stat statbuf; 730 | if (stat(path, &statbuf) < 0) { 731 | nob_log(NOB_ERROR, "Could not get stat of %s: %s", path, strerror(errno)); 732 | return -1; 733 | } 734 | 735 | switch (statbuf.st_mode & S_IFMT) { 736 | case S_IFDIR: return NOB_FILE_DIRECTORY; 737 | case S_IFREG: return NOB_FILE_REGULAR; 738 | case S_IFLNK: return NOB_FILE_SYMLINK; 739 | default: return NOB_FILE_OTHER; 740 | } 741 | #endif // _WIN32 742 | } 743 | 744 | bool nob_copy_directory_recursively(const char *src_path, const char *dst_path) 745 | { 746 | bool result = true; 747 | Nob_File_Paths children = {0}; 748 | Nob_String_Builder src_sb = {0}; 749 | Nob_String_Builder dst_sb = {0}; 750 | size_t temp_checkpoint = nob_temp_save(); 751 | 752 | Nob_File_Type type = nob_get_file_type(src_path); 753 | if (type < 0) return false; 754 | 755 | switch (type) { 756 | case NOB_FILE_DIRECTORY: { 757 | if (!nob_mkdir_if_not_exists(dst_path)) nob_return_defer(false); 758 | if (!nob_read_entire_dir(src_path, &children)) nob_return_defer(false); 759 | 760 | for (size_t i = 0; i < children.count; ++i) { 761 | if (strcmp(children.items[i], ".") == 0) continue; 762 | if (strcmp(children.items[i], "..") == 0) continue; 763 | 764 | src_sb.count = 0; 765 | nob_sb_append_cstr(&src_sb, src_path); 766 | nob_sb_append_cstr(&src_sb, "/"); 767 | nob_sb_append_cstr(&src_sb, children.items[i]); 768 | nob_sb_append_null(&src_sb); 769 | 770 | dst_sb.count = 0; 771 | nob_sb_append_cstr(&dst_sb, dst_path); 772 | nob_sb_append_cstr(&dst_sb, "/"); 773 | nob_sb_append_cstr(&dst_sb, children.items[i]); 774 | nob_sb_append_null(&dst_sb); 775 | 776 | if (!nob_copy_directory_recursively(src_sb.items, dst_sb.items)) { 777 | nob_return_defer(false); 778 | } 779 | } 780 | } break; 781 | 782 | case NOB_FILE_REGULAR: { 783 | if (!nob_copy_file(src_path, dst_path)) { 784 | nob_return_defer(false); 785 | } 786 | } break; 787 | 788 | case NOB_FILE_SYMLINK: { 789 | nob_log(NOB_WARNING, "TODO: Copying symlinks is not supported yet"); 790 | } break; 791 | 792 | case NOB_FILE_OTHER: { 793 | nob_log(NOB_ERROR, "Unsupported type of file %s", src_path); 794 | nob_return_defer(false); 795 | } break; 796 | 797 | default: NOB_ASSERT(0 && "unreachable"); 798 | } 799 | 800 | defer: 801 | nob_temp_rewind(temp_checkpoint); 802 | nob_da_free(src_sb); 803 | nob_da_free(dst_sb); 804 | nob_da_free(children); 805 | return result; 806 | } 807 | 808 | char *nob_temp_strdup(const char *cstr) 809 | { 810 | size_t n = strlen(cstr); 811 | char *result = nob_temp_alloc(n + 1); 812 | NOB_ASSERT(result != NULL && "Increase NOB_TEMP_CAPACITY"); 813 | memcpy(result, cstr, n); 814 | result[n] = '\0'; 815 | return result; 816 | } 817 | 818 | void *nob_temp_alloc(size_t size) 819 | { 820 | if (nob_temp_size + size > NOB_TEMP_CAPACITY) return NULL; 821 | void *result = &nob_temp[nob_temp_size]; 822 | nob_temp_size += size; 823 | return result; 824 | } 825 | 826 | char *nob_temp_sprintf(const char *format, ...) 827 | { 828 | va_list args; 829 | va_start(args, format); 830 | int n = vsnprintf(NULL, 0, format, args); 831 | va_end(args); 832 | 833 | NOB_ASSERT(n >= 0); 834 | char *result = nob_temp_alloc(n + 1); 835 | NOB_ASSERT(result != NULL && "Extend the size of the temporary allocator"); 836 | // TODO: use proper arenas for the temporary allocator; 837 | va_start(args, format); 838 | vsnprintf(result, n + 1, format, args); 839 | va_end(args); 840 | 841 | return result; 842 | } 843 | 844 | void nob_temp_reset(void) 845 | { 846 | nob_temp_size = 0; 847 | } 848 | 849 | size_t nob_temp_save(void) 850 | { 851 | return nob_temp_size; 852 | } 853 | 854 | void nob_temp_rewind(size_t checkpoint) 855 | { 856 | nob_temp_size = checkpoint; 857 | } 858 | 859 | const char *nob_temp_sv_to_cstr(Nob_String_View sv) 860 | { 861 | char *result = nob_temp_alloc(sv.count + 1); 862 | NOB_ASSERT(result != NULL && "Extend the size of the temporary allocator"); 863 | memcpy(result, sv.data, sv.count); 864 | result[sv.count] = '\0'; 865 | return result; 866 | } 867 | 868 | int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t input_paths_count) 869 | { 870 | #ifdef _WIN32 871 | BOOL bSuccess; 872 | 873 | HANDLE output_path_fd = CreateFile(output_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); 874 | if (output_path_fd == INVALID_HANDLE_VALUE) { 875 | // NOTE: if output does not exist it 100% must be rebuilt 876 | if (GetLastError() == ERROR_FILE_NOT_FOUND) return 1; 877 | nob_log(NOB_ERROR, "Could not open file %s: %lu", output_path, GetLastError()); 878 | return -1; 879 | } 880 | FILETIME output_path_time; 881 | bSuccess = GetFileTime(output_path_fd, NULL, NULL, &output_path_time); 882 | CloseHandle(output_path_fd); 883 | if (!bSuccess) { 884 | nob_log(NOB_ERROR, "Could not get time of %s: %lu", output_path, GetLastError()); 885 | return -1; 886 | } 887 | 888 | for (size_t i = 0; i < input_paths_count; ++i) { 889 | const char *input_path = input_paths[i]; 890 | HANDLE input_path_fd = CreateFile(input_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); 891 | if (input_path_fd == INVALID_HANDLE_VALUE) { 892 | // NOTE: non-existing input is an error cause it is needed for building in the first place 893 | nob_log(NOB_ERROR, "Could not open file %s: %lu", input_path, GetLastError()); 894 | return -1; 895 | } 896 | FILETIME input_path_time; 897 | bSuccess = GetFileTime(input_path_fd, NULL, NULL, &input_path_time); 898 | CloseHandle(input_path_fd); 899 | if (!bSuccess) { 900 | nob_log(NOB_ERROR, "Could not get time of %s: %lu", input_path, GetLastError()); 901 | return -1; 902 | } 903 | 904 | // NOTE: if even a single input_path is fresher than output_path that's 100% rebuild 905 | if (CompareFileTime(&input_path_time, &output_path_time) == 1) return 1; 906 | } 907 | 908 | return 0; 909 | #else 910 | struct stat statbuf = {0}; 911 | 912 | if (stat(output_path, &statbuf) < 0) { 913 | // NOTE: if output does not exist it 100% must be rebuilt 914 | if (errno == ENOENT) return 1; 915 | nob_log(NOB_ERROR, "could not stat %s: %s", output_path, strerror(errno)); 916 | return -1; 917 | } 918 | int output_path_time = statbuf.st_mtime; 919 | 920 | for (size_t i = 0; i < input_paths_count; ++i) { 921 | const char *input_path = input_paths[i]; 922 | if (stat(input_path, &statbuf) < 0) { 923 | // NOTE: non-existing input is an error cause it is needed for building in the first place 924 | nob_log(NOB_ERROR, "could not stat %s: %s", input_path, strerror(errno)); 925 | return -1; 926 | } 927 | int input_path_time = statbuf.st_mtime; 928 | // NOTE: if even a single input_path is fresher than output_path that's 100% rebuild 929 | if (input_path_time > output_path_time) return 1; 930 | } 931 | 932 | return 0; 933 | #endif 934 | } 935 | 936 | int nob_needs_rebuild1(const char *output_path, const char *input_path) 937 | { 938 | return nob_needs_rebuild(output_path, &input_path, 1); 939 | } 940 | 941 | bool nob_rename(const char *old_path, const char *new_path) 942 | { 943 | nob_log(NOB_INFO, "renaming %s -> %s", old_path, new_path); 944 | #ifdef _WIN32 945 | if (!MoveFileEx(old_path, new_path, MOVEFILE_REPLACE_EXISTING)) { 946 | nob_log(NOB_ERROR, "could not rename %s to %s: %lu", old_path, new_path, GetLastError()); 947 | return false; 948 | } 949 | #else 950 | if (rename(old_path, new_path) < 0) { 951 | nob_log(NOB_ERROR, "could not rename %s to %s: %s", old_path, new_path, strerror(errno)); 952 | return false; 953 | } 954 | #endif // _WIN32 955 | return true; 956 | } 957 | 958 | bool nob_read_entire_file(const char *path, Nob_String_Builder *sb) 959 | { 960 | bool result = true; 961 | 962 | FILE *f = fopen(path, "rb"); 963 | if (f == NULL) nob_return_defer(false); 964 | if (fseek(f, 0, SEEK_END) < 0) nob_return_defer(false); 965 | long m = ftell(f); 966 | if (m < 0) nob_return_defer(false); 967 | if (fseek(f, 0, SEEK_SET) < 0) nob_return_defer(false); 968 | 969 | size_t new_count = sb->count + m; 970 | if (new_count > sb->capacity) { 971 | sb->items = realloc(sb->items, new_count); 972 | NOB_ASSERT(sb->items != NULL && "Buy more RAM lool!!"); 973 | sb->capacity = new_count; 974 | } 975 | 976 | fread(sb->items + sb->count, m, 1, f); 977 | if (ferror(f)) { 978 | // TODO: Afaik, ferror does not set errno. So the error reporting in defer is not correct in this case. 979 | nob_return_defer(false); 980 | } 981 | sb->count = new_count; 982 | 983 | defer: 984 | if (!result) nob_log(NOB_ERROR, "Could not read file %s: %s", path, strerror(errno)); 985 | if (f) fclose(f); 986 | return result; 987 | } 988 | 989 | Nob_String_View nob_sv_chop_by_delim(Nob_String_View *sv, char delim) 990 | { 991 | size_t i = 0; 992 | while (i < sv->count && sv->data[i] != delim) { 993 | i += 1; 994 | } 995 | 996 | Nob_String_View result = nob_sv_from_parts(sv->data, i); 997 | 998 | if (i < sv->count) { 999 | sv->count -= i + 1; 1000 | sv->data += i + 1; 1001 | } else { 1002 | sv->count -= i; 1003 | sv->data += i; 1004 | } 1005 | 1006 | return result; 1007 | } 1008 | 1009 | Nob_String_View nob_sv_from_parts(const char *data, size_t count) 1010 | { 1011 | Nob_String_View sv; 1012 | sv.count = count; 1013 | sv.data = data; 1014 | return sv; 1015 | } 1016 | 1017 | Nob_String_View nob_sv_trim_left(Nob_String_View sv) 1018 | { 1019 | size_t i = 0; 1020 | while (i < sv.count && isspace(sv.data[i])) { 1021 | i += 1; 1022 | } 1023 | 1024 | return nob_sv_from_parts(sv.data + i, sv.count - i); 1025 | } 1026 | 1027 | Nob_String_View nob_sv_trim_right(Nob_String_View sv) 1028 | { 1029 | size_t i = 0; 1030 | while (i < sv.count && isspace(sv.data[sv.count - 1 - i])) { 1031 | i += 1; 1032 | } 1033 | 1034 | return nob_sv_from_parts(sv.data, sv.count - i); 1035 | } 1036 | 1037 | Nob_String_View nob_sv_trim(Nob_String_View sv) 1038 | { 1039 | return nob_sv_trim_right(nob_sv_trim_left(sv)); 1040 | } 1041 | 1042 | Nob_String_View nob_sv_from_cstr(const char *cstr) 1043 | { 1044 | return nob_sv_from_parts(cstr, strlen(cstr)); 1045 | } 1046 | 1047 | bool nob_sv_eq(Nob_String_View a, Nob_String_View b) 1048 | { 1049 | if (a.count != b.count) { 1050 | return false; 1051 | } else { 1052 | return memcmp(a.data, b.data, a.count) == 0; 1053 | } 1054 | } 1055 | 1056 | // RETURNS: 1057 | // 0 - file does not exists 1058 | // 1 - file exists 1059 | // -1 - error while checking if file exists. The error is logged 1060 | int nob_file_exists(const char *file_path) 1061 | { 1062 | #if _WIN32 1063 | // TODO: distinguish between "does not exists" and other errors 1064 | DWORD dwAttrib = GetFileAttributesA(file_path); 1065 | return dwAttrib != INVALID_FILE_ATTRIBUTES; 1066 | #else 1067 | struct stat statbuf; 1068 | if (stat(file_path, &statbuf) < 0) { 1069 | if (errno == ENOENT) return 0; 1070 | nob_log(NOB_ERROR, "Could not check if file %s exists: %s", file_path, strerror(errno)); 1071 | return -1; 1072 | } 1073 | return 1; 1074 | #endif 1075 | } 1076 | 1077 | // minirent.h SOURCE BEGIN //////////////////////////////////////// 1078 | #ifdef _WIN32 1079 | struct DIR 1080 | { 1081 | HANDLE hFind; 1082 | WIN32_FIND_DATA data; 1083 | struct dirent *dirent; 1084 | }; 1085 | 1086 | DIR *opendir(const char *dirpath) 1087 | { 1088 | assert(dirpath); 1089 | 1090 | char buffer[MAX_PATH]; 1091 | snprintf(buffer, MAX_PATH, "%s\\*", dirpath); 1092 | 1093 | DIR *dir = (DIR*)calloc(1, sizeof(DIR)); 1094 | 1095 | dir->hFind = FindFirstFile(buffer, &dir->data); 1096 | if (dir->hFind == INVALID_HANDLE_VALUE) { 1097 | // TODO: opendir should set errno accordingly on FindFirstFile fail 1098 | // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror 1099 | errno = ENOSYS; 1100 | goto fail; 1101 | } 1102 | 1103 | return dir; 1104 | 1105 | fail: 1106 | if (dir) { 1107 | free(dir); 1108 | } 1109 | 1110 | return NULL; 1111 | } 1112 | 1113 | struct dirent *readdir(DIR *dirp) 1114 | { 1115 | assert(dirp); 1116 | 1117 | if (dirp->dirent == NULL) { 1118 | dirp->dirent = (struct dirent*)calloc(1, sizeof(struct dirent)); 1119 | } else { 1120 | if(!FindNextFile(dirp->hFind, &dirp->data)) { 1121 | if (GetLastError() != ERROR_NO_MORE_FILES) { 1122 | // TODO: readdir should set errno accordingly on FindNextFile fail 1123 | // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror 1124 | errno = ENOSYS; 1125 | } 1126 | 1127 | return NULL; 1128 | } 1129 | } 1130 | 1131 | memset(dirp->dirent->d_name, 0, sizeof(dirp->dirent->d_name)); 1132 | 1133 | strncpy( 1134 | dirp->dirent->d_name, 1135 | dirp->data.cFileName, 1136 | sizeof(dirp->dirent->d_name) - 1); 1137 | 1138 | return dirp->dirent; 1139 | } 1140 | 1141 | int closedir(DIR *dirp) 1142 | { 1143 | assert(dirp); 1144 | 1145 | if(!FindClose(dirp->hFind)) { 1146 | // TODO: closedir should set errno accordingly on FindClose fail 1147 | // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror 1148 | errno = ENOSYS; 1149 | return -1; 1150 | } 1151 | 1152 | if (dirp->dirent) { 1153 | free(dirp->dirent); 1154 | } 1155 | free(dirp); 1156 | 1157 | return 0; 1158 | } 1159 | #endif // _WIN32 1160 | // minirent.h SOURCE END //////////////////////////////////////// 1161 | 1162 | #endif 1163 | -------------------------------------------------------------------------------- /qlock.c: -------------------------------------------------------------------------------- 1 | char *s = "?"; 2 | x, y, d[8], i, dx; 3 | f[] = {31599,19812,14479,31207,23524,29411,29679,30866,31727,31719,1040}; 4 | char *so, *si; 5 | p(ch) 6 | { 7 | i = x/2/(3 + 2); 8 | dx = x/2%(3 + 2); 9 | if (i < 8 && (y - 4)/2 < 5 && dx < 3 && (f[d[i]]>>((5 - (y - 4)/2 - 1)*3 + dx))&1) 10 | printf("\033[1;41;30m%c\033[0m", ch); 11 | else 12 | printf("%c", ch); 13 | if (ch == '\n') { 14 | y += 1; 15 | x = 0; 16 | } else 17 | x += 1; 18 | } 19 | gd() 20 | { 21 | time_t t = time(NULL); 22 | struct tm *tm = localtime(&t); 23 | d[0] = tm->tm_hour/10; 24 | d[1] = tm->tm_hour%10; 25 | d[2] = 10; 26 | d[3] = tm->tm_min/10; 27 | d[4] = tm->tm_min%10; 28 | d[5] = 10; 29 | d[6] = tm->tm_sec/10; 30 | d[7] = tm->tm_sec%10; 31 | } 32 | 33 | main() 34 | { 35 | for (gd();;printf("\n\033[%dA\033[%dD", y + 1, x), sleep(1), gd()) 36 | for (so = s, x = 0, y = 0; *so; so++) 37 | if (*so == 63) 38 | for (si = s; *si; si++) 39 | switch (*si) { 40 | case '\n': p('\\'); p('n'); p('"'); p('\n'); p('"'); break; 41 | case '"': p('\\'); p('\"'); break; 42 | case '\\': p('\\'); p('\\'); break; 43 | default: p(*si); 44 | } 45 | else p(*so); 46 | } 47 | -------------------------------------------------------------------------------- /quine.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | const char *src = "#include \n#include \n\nint main()\n{\n const char *src = \"?\";\n size_t src_n = strlen(src);\n for (size_t i = 0; i < src_n; ++i) {\n if (src[i] == 63) {\n for (size_t j = 0; j < src_n; ++j) {\n switch (src[j]) {\n case '\\n': printf(\"\\\\n\"); break;\n case '\"': printf(\"\\\\\\\"\"); break;\n case '\\\\': printf(\"\\\\\\\\\"); break;\n default: printf(\"%c\", src[j]);\n }\n }\n } else {\n printf(\"%c\", src[i]);\n }\n }\n return 0;\n}\n"; 7 | size_t src_n = strlen(src); 8 | for (size_t i = 0; i < src_n; ++i) { 9 | if (src[i] == 63) { 10 | for (size_t j = 0; j < src_n; ++j) { 11 | switch (src[j]) { 12 | case '\n': printf("\\n"); break; 13 | case '"': printf("\\\""); break; 14 | case '\\': printf("\\\\"); break; 15 | default: printf("%c", src[j]); 16 | } 17 | } 18 | } else { 19 | printf("%c", src[i]); 20 | } 21 | } 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /stb_c_lexer.h: -------------------------------------------------------------------------------- 1 | // stb_c_lexer.h - v0.12 - public domain Sean Barrett 2013 2 | // lexer for making little C-like languages with recursive-descent parsers 3 | // 4 | // This file provides both the interface and the implementation. 5 | // To instantiate the implementation, 6 | // #define STB_C_LEXER_IMPLEMENTATION 7 | // in *ONE* source file, before #including this file. 8 | // 9 | // The default configuration is fairly close to a C lexer, although 10 | // suffixes on integer constants are not handled (you can override this). 11 | // 12 | // History: 13 | // 0.12 fix compilation bug for NUL support; better support separate inclusion 14 | // 0.11 fix clang static analysis warning 15 | // 0.10 fix warnings 16 | // 0.09 hex floats, no-stdlib fixes 17 | // 0.08 fix bad pointer comparison 18 | // 0.07 fix mishandling of hexadecimal constants parsed by strtol 19 | // 0.06 fix missing next character after ending quote mark (Andreas Fredriksson) 20 | // 0.05 refixed get_location because github version had lost the fix 21 | // 0.04 fix octal parsing bug 22 | // 0.03 added STB_C_LEX_DISCARD_PREPROCESSOR option 23 | // refactor API to simplify (only one struct instead of two) 24 | // change literal enum names to have 'lit' at the end 25 | // 0.02 first public release 26 | // 27 | // Status: 28 | // - haven't tested compiling as C++ 29 | // - haven't tested the float parsing path 30 | // - haven't tested the non-default-config paths (e.g. non-stdlib) 31 | // - only tested default-config paths by eyeballing output of self-parse 32 | // 33 | // - haven't implemented multiline strings 34 | // - haven't implemented octal/hex character constants 35 | // - haven't implemented support for unicode CLEX_char 36 | // - need to expand error reporting so you don't just get "CLEX_parse_error" 37 | // 38 | // Contributors: 39 | // Arpad Goretity (bugfix) 40 | // Alan Hickman (hex floats) 41 | // 42 | // LICENSE 43 | // 44 | // See end of file for license information. 45 | 46 | #ifdef STB_C_LEXER_IMPLEMENTATION 47 | #ifndef STB_C_LEXER_DEFINITIONS 48 | // to change the default parsing rules, copy the following lines 49 | // into your C/C++ file *before* including this, and then replace 50 | // the Y's with N's for the ones you don't want. This needs to be 51 | // set to the same values for every place in your program where 52 | // stb_c_lexer.h is included. 53 | // --BEGIN-- 54 | 55 | #if defined(Y) || defined(N) 56 | #error "Can only use stb_c_lexer in contexts where the preprocessor symbols 'Y' and 'N' are not defined" 57 | #endif 58 | 59 | #define STB_C_LEX_C_DECIMAL_INTS Y // "0|[1-9][0-9]*" CLEX_intlit 60 | #define STB_C_LEX_C_HEX_INTS Y // "0x[0-9a-fA-F]+" CLEX_intlit 61 | #define STB_C_LEX_C_OCTAL_INTS Y // "[0-7]+" CLEX_intlit 62 | #define STB_C_LEX_C_DECIMAL_FLOATS Y // "[0-9]*(.[0-9]*([eE][-+]?[0-9]+)?) CLEX_floatlit 63 | #define STB_C_LEX_C99_HEX_FLOATS N // "0x{hex}+(.{hex}*)?[pP][-+]?{hex}+ CLEX_floatlit 64 | #define STB_C_LEX_C_IDENTIFIERS Y // "[_a-zA-Z][_a-zA-Z0-9]*" CLEX_id 65 | #define STB_C_LEX_C_DQ_STRINGS Y // double-quote-delimited strings with escapes CLEX_dqstring 66 | #define STB_C_LEX_C_SQ_STRINGS N // single-quote-delimited strings with escapes CLEX_ssstring 67 | #define STB_C_LEX_C_CHARS Y // single-quote-delimited character with escape CLEX_charlits 68 | #define STB_C_LEX_C_COMMENTS Y // "/* comment */" 69 | #define STB_C_LEX_CPP_COMMENTS Y // "// comment to end of line\n" 70 | #define STB_C_LEX_C_COMPARISONS Y // "==" CLEX_eq "!=" CLEX_noteq "<=" CLEX_lesseq ">=" CLEX_greatereq 71 | #define STB_C_LEX_C_LOGICAL Y // "&&" CLEX_andand "||" CLEX_oror 72 | #define STB_C_LEX_C_SHIFTS Y // "<<" CLEX_shl ">>" CLEX_shr 73 | #define STB_C_LEX_C_INCREMENTS Y // "++" CLEX_plusplus "--" CLEX_minusminus 74 | #define STB_C_LEX_C_ARROW Y // "->" CLEX_arrow 75 | #define STB_C_LEX_EQUAL_ARROW N // "=>" CLEX_eqarrow 76 | #define STB_C_LEX_C_BITWISEEQ Y // "&=" CLEX_andeq "|=" CLEX_oreq "^=" CLEX_xoreq 77 | #define STB_C_LEX_C_ARITHEQ Y // "+=" CLEX_pluseq "-=" CLEX_minuseq 78 | // "*=" CLEX_muleq "/=" CLEX_diveq "%=" CLEX_modeq 79 | // if both STB_C_LEX_SHIFTS & STB_C_LEX_ARITHEQ: 80 | // "<<=" CLEX_shleq ">>=" CLEX_shreq 81 | 82 | #define STB_C_LEX_PARSE_SUFFIXES N // letters after numbers are parsed as part of those numbers, and must be in suffix list below 83 | #define STB_C_LEX_DECIMAL_SUFFIXES "" // decimal integer suffixes e.g. "uUlL" -- these are returned as-is in string storage 84 | #define STB_C_LEX_HEX_SUFFIXES "" // e.g. "uUlL" 85 | #define STB_C_LEX_OCTAL_SUFFIXES "" // e.g. "uUlL" 86 | #define STB_C_LEX_FLOAT_SUFFIXES "" // 87 | 88 | #define STB_C_LEX_0_IS_EOF N // if Y, ends parsing at '\0'; if N, returns '\0' as token 89 | #define STB_C_LEX_INTEGERS_AS_DOUBLES N // parses integers as doubles so they can be larger than 'int', but only if STB_C_LEX_STDLIB==N 90 | #define STB_C_LEX_MULTILINE_DSTRINGS N // allow newlines in double-quoted strings 91 | #define STB_C_LEX_MULTILINE_SSTRINGS N // allow newlines in single-quoted strings 92 | #define STB_C_LEX_USE_STDLIB Y // use strtod,strtol for parsing #s; otherwise inaccurate hack 93 | #define STB_C_LEX_DOLLAR_IDENTIFIER Y // allow $ as an identifier character 94 | #define STB_C_LEX_FLOAT_NO_DECIMAL Y // allow floats that have no decimal point if they have an exponent 95 | 96 | #define STB_C_LEX_DEFINE_ALL_TOKEN_NAMES N // if Y, all CLEX_ token names are defined, even if never returned 97 | // leaving it as N should help you catch config bugs 98 | 99 | #define STB_C_LEX_DISCARD_PREPROCESSOR Y // discard C-preprocessor directives (e.g. after prepocess 100 | // still have #line, #pragma, etc) 101 | 102 | //#define STB_C_LEX_ISWHITE(str) ... // return length in bytes of whitespace characters if first char is whitespace 103 | 104 | #define STB_C_LEXER_DEFINITIONS // This line prevents the header file from replacing your definitions 105 | // --END-- 106 | #endif 107 | #endif 108 | 109 | #ifndef INCLUDE_STB_C_LEXER_H 110 | #define INCLUDE_STB_C_LEXER_H 111 | 112 | typedef struct 113 | { 114 | // lexer variables 115 | char *input_stream; 116 | char *eof; 117 | char *parse_point; 118 | char *string_storage; 119 | int string_storage_len; 120 | 121 | // lexer parse location for error messages 122 | char *where_firstchar; 123 | char *where_lastchar; 124 | 125 | // lexer token variables 126 | long token; 127 | double real_number; 128 | long int_number; 129 | char *string; 130 | int string_len; 131 | } stb_lexer; 132 | 133 | typedef struct 134 | { 135 | int line_number; 136 | int line_offset; 137 | } stb_lex_location; 138 | 139 | #ifdef __cplusplus 140 | extern "C" { 141 | #endif 142 | 143 | extern void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length); 144 | // this function initialize the 'lexer' structure 145 | // Input: 146 | // - input_stream points to the file to parse, loaded into memory 147 | // - input_stream_end points to the end of the file, or NULL if you use 0-for-EOF 148 | // - string_store is storage the lexer can use for storing parsed strings and identifiers 149 | // - store_length is the length of that storage 150 | 151 | extern int stb_c_lexer_get_token(stb_lexer *lexer); 152 | // this function returns non-zero if a token is parsed, or 0 if at EOF 153 | // Output: 154 | // - lexer->token is the token ID, which is unicode code point for a single-char token, < 0 for a multichar or eof or error 155 | // - lexer->real_number is a double constant value for CLEX_floatlit, or CLEX_intlit if STB_C_LEX_INTEGERS_AS_DOUBLES 156 | // - lexer->int_number is an integer constant for CLEX_intlit if !STB_C_LEX_INTEGERS_AS_DOUBLES, or character for CLEX_charlit 157 | // - lexer->string is a 0-terminated string for CLEX_dqstring or CLEX_sqstring or CLEX_identifier 158 | // - lexer->string_len is the byte length of lexer->string 159 | 160 | extern void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc); 161 | // this inefficient function returns the line number and character offset of a 162 | // given location in the file as returned by stb_lex_token. Because it's inefficient, 163 | // you should only call it for errors, not for every token. 164 | // For error messages of invalid tokens, you typically want the location of the start 165 | // of the token (which caused the token to be invalid). For bugs involving legit 166 | // tokens, you can report the first or the range. 167 | // Output: 168 | // - loc->line_number is the line number in the file, counting from 1, of the location 169 | // - loc->line_offset is the char-offset in the line, counting from 0, of the location 170 | 171 | 172 | #ifdef __cplusplus 173 | } 174 | #endif 175 | 176 | enum 177 | { 178 | CLEX_eof = 256, 179 | CLEX_parse_error, 180 | CLEX_intlit , 181 | CLEX_floatlit , 182 | CLEX_id , 183 | CLEX_dqstring , 184 | CLEX_sqstring , 185 | CLEX_charlit , 186 | CLEX_eq , 187 | CLEX_noteq , 188 | CLEX_lesseq , 189 | CLEX_greatereq , 190 | CLEX_andand , 191 | CLEX_oror , 192 | CLEX_shl , 193 | CLEX_shr , 194 | CLEX_plusplus , 195 | CLEX_minusminus , 196 | CLEX_pluseq , 197 | CLEX_minuseq , 198 | CLEX_muleq , 199 | CLEX_diveq , 200 | CLEX_modeq , 201 | CLEX_andeq , 202 | CLEX_oreq , 203 | CLEX_xoreq , 204 | CLEX_arrow , 205 | CLEX_eqarrow , 206 | CLEX_shleq, CLEX_shreq, 207 | 208 | CLEX_first_unused_token 209 | 210 | }; 211 | #endif // INCLUDE_STB_C_LEXER_H 212 | 213 | #ifdef STB_C_LEXER_IMPLEMENTATION 214 | 215 | // Hacky definitions so we can easily #if on them 216 | #define Y(x) 1 217 | #define N(x) 0 218 | 219 | #if STB_C_LEX_INTEGERS_AS_DOUBLES(x) 220 | typedef double stb__clex_int; 221 | #define intfield real_number 222 | #define STB__clex_int_as_double 223 | #else 224 | typedef long stb__clex_int; 225 | #define intfield int_number 226 | #endif 227 | 228 | // Convert these config options to simple conditional #defines so we can more 229 | // easily test them once we've change the meaning of Y/N 230 | 231 | #if STB_C_LEX_PARSE_SUFFIXES(x) 232 | #define STB__clex_parse_suffixes 233 | #endif 234 | 235 | #if STB_C_LEX_C99_HEX_FLOATS(x) 236 | #define STB__clex_hex_floats 237 | #endif 238 | 239 | #if STB_C_LEX_C_HEX_INTS(x) 240 | #define STB__clex_hex_ints 241 | #endif 242 | 243 | #if STB_C_LEX_C_DECIMAL_INTS(x) 244 | #define STB__clex_decimal_ints 245 | #endif 246 | 247 | #if STB_C_LEX_C_OCTAL_INTS(x) 248 | #define STB__clex_octal_ints 249 | #endif 250 | 251 | #if STB_C_LEX_C_DECIMAL_FLOATS(x) 252 | #define STB__clex_decimal_floats 253 | #endif 254 | 255 | #if STB_C_LEX_DISCARD_PREPROCESSOR(x) 256 | #define STB__clex_discard_preprocessor 257 | #endif 258 | 259 | #if STB_C_LEX_USE_STDLIB(x) && (!defined(STB__clex_hex_floats) || __STDC_VERSION__ >= 199901L) 260 | #define STB__CLEX_use_stdlib 261 | #include 262 | #endif 263 | 264 | // Now for the rest of the file we'll use the basic definition where 265 | // where Y expands to its contents and N expands to nothing 266 | #undef Y 267 | #define Y(a) a 268 | #undef N 269 | #define N(a) 270 | 271 | // API function 272 | void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length) 273 | { 274 | lexer->input_stream = (char *) input_stream; 275 | lexer->eof = (char *) input_stream_end; 276 | lexer->parse_point = (char *) input_stream; 277 | lexer->string_storage = string_store; 278 | lexer->string_storage_len = store_length; 279 | } 280 | 281 | // API function 282 | void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc) 283 | { 284 | char *p = lexer->input_stream; 285 | int line_number = 1; 286 | int char_offset = 0; 287 | while (*p && p < where) { 288 | if (*p == '\n' || *p == '\r') { 289 | p += (p[0]+p[1] == '\r'+'\n' ? 2 : 1); // skip newline 290 | line_number += 1; 291 | char_offset = 0; 292 | } else { 293 | ++p; 294 | ++char_offset; 295 | } 296 | } 297 | loc->line_number = line_number; 298 | loc->line_offset = char_offset; 299 | } 300 | 301 | // main helper function for returning a parsed token 302 | static int stb__clex_token(stb_lexer *lexer, int token, char *start, char *end) 303 | { 304 | lexer->token = token; 305 | lexer->where_firstchar = start; 306 | lexer->where_lastchar = end; 307 | lexer->parse_point = end+1; 308 | return 1; 309 | } 310 | 311 | // helper function for returning eof 312 | static int stb__clex_eof(stb_lexer *lexer) 313 | { 314 | lexer->token = CLEX_eof; 315 | return 0; 316 | } 317 | 318 | static int stb__clex_iswhite(int x) 319 | { 320 | return x == ' ' || x == '\t' || x == '\r' || x == '\n' || x == '\f'; 321 | } 322 | 323 | static const char *stb__strchr(const char *str, int ch) 324 | { 325 | for (; *str; ++str) 326 | if (*str == ch) 327 | return str; 328 | return 0; 329 | } 330 | 331 | // parse suffixes at the end of a number 332 | static int stb__clex_parse_suffixes(stb_lexer *lexer, long tokenid, char *start, char *cur, const char *suffixes) 333 | { 334 | #ifdef STB__clex_parse_suffixes 335 | lexer->string = lexer->string_storage; 336 | lexer->string_len = 0; 337 | 338 | while ((*cur >= 'a' && *cur <= 'z') || (*cur >= 'A' && *cur <= 'Z')) { 339 | if (stb__strchr(suffixes, *cur) == 0) 340 | return stb__clex_token(lexer, CLEX_parse_error, start, cur); 341 | if (lexer->string_len+1 >= lexer->string_storage_len) 342 | return stb__clex_token(lexer, CLEX_parse_error, start, cur); 343 | lexer->string[lexer->string_len++] = *cur++; 344 | } 345 | #else 346 | suffixes = suffixes; // attempt to suppress warnings 347 | #endif 348 | return stb__clex_token(lexer, tokenid, start, cur-1); 349 | } 350 | 351 | #ifndef STB__CLEX_use_stdlib 352 | static double stb__clex_pow(double base, unsigned int exponent) 353 | { 354 | double value=1; 355 | for ( ; exponent; exponent >>= 1) { 356 | if (exponent & 1) 357 | value *= base; 358 | base *= base; 359 | } 360 | return value; 361 | } 362 | 363 | static double stb__clex_parse_float(char *p, char **q) 364 | { 365 | char *s = p; 366 | double value=0; 367 | int base=10; 368 | int exponent=0; 369 | 370 | #ifdef STB__clex_hex_floats 371 | if (*p == '0') { 372 | if (p[1] == 'x' || p[1] == 'X') { 373 | base=16; 374 | p += 2; 375 | } 376 | } 377 | #endif 378 | 379 | for (;;) { 380 | if (*p >= '0' && *p <= '9') 381 | value = value*base + (*p++ - '0'); 382 | #ifdef STB__clex_hex_floats 383 | else if (base == 16 && *p >= 'a' && *p <= 'f') 384 | value = value*base + 10 + (*p++ - 'a'); 385 | else if (base == 16 && *p >= 'A' && *p <= 'F') 386 | value = value*base + 10 + (*p++ - 'A'); 387 | #endif 388 | else 389 | break; 390 | } 391 | 392 | if (*p == '.') { 393 | double pow, addend = 0; 394 | ++p; 395 | for (pow=1; ; pow*=base) { 396 | if (*p >= '0' && *p <= '9') 397 | addend = addend*base + (*p++ - '0'); 398 | #ifdef STB__clex_hex_floats 399 | else if (base == 16 && *p >= 'a' && *p <= 'f') 400 | addend = addend*base + 10 + (*p++ - 'a'); 401 | else if (base == 16 && *p >= 'A' && *p <= 'F') 402 | addend = addend*base + 10 + (*p++ - 'A'); 403 | #endif 404 | else 405 | break; 406 | } 407 | value += addend / pow; 408 | } 409 | #ifdef STB__clex_hex_floats 410 | if (base == 16) { 411 | // exponent required for hex float literal 412 | if (*p != 'p' && *p != 'P') { 413 | *q = s; 414 | return 0; 415 | } 416 | exponent = 1; 417 | } else 418 | #endif 419 | exponent = (*p == 'e' || *p == 'E'); 420 | 421 | if (exponent) { 422 | int sign = p[1] == '-'; 423 | unsigned int exponent=0; 424 | double power=1; 425 | ++p; 426 | if (*p == '-' || *p == '+') 427 | ++p; 428 | while (*p >= '0' && *p <= '9') 429 | exponent = exponent*10 + (*p++ - '0'); 430 | 431 | #ifdef STB__clex_hex_floats 432 | if (base == 16) 433 | power = stb__clex_pow(2, exponent); 434 | else 435 | #endif 436 | power = stb__clex_pow(10, exponent); 437 | if (sign) 438 | value /= power; 439 | else 440 | value *= power; 441 | } 442 | *q = p; 443 | return value; 444 | } 445 | #endif 446 | 447 | static int stb__clex_parse_char(char *p, char **q) 448 | { 449 | if (*p == '\\') { 450 | *q = p+2; // tentatively guess we'll parse two characters 451 | switch(p[1]) { 452 | case '\\': return '\\'; 453 | case '\'': return '\''; 454 | case '"': return '"'; 455 | case 't': return '\t'; 456 | case 'f': return '\f'; 457 | case 'n': return '\n'; 458 | case 'r': return '\r'; 459 | case '0': return '\0'; // @TODO ocatal constants 460 | case 'x': case 'X': return -1; // @TODO hex constants 461 | case 'u': return -1; // @TODO unicode constants 462 | } 463 | } 464 | *q = p+1; 465 | return (unsigned char) *p; 466 | } 467 | 468 | static int stb__clex_parse_string(stb_lexer *lexer, char *p, int type) 469 | { 470 | char *start = p; 471 | char delim = *p++; // grab the " or ' for later matching 472 | char *out = lexer->string_storage; 473 | char *outend = lexer->string_storage + lexer->string_storage_len; 474 | while (*p != delim) { 475 | int n; 476 | if (*p == '\\') { 477 | char *q; 478 | n = stb__clex_parse_char(p, &q); 479 | if (n < 0) 480 | return stb__clex_token(lexer, CLEX_parse_error, start, q); 481 | p = q; 482 | } else { 483 | // @OPTIMIZE: could speed this up by looping-while-not-backslash 484 | n = (unsigned char) *p++; 485 | } 486 | if (out+1 > outend) 487 | return stb__clex_token(lexer, CLEX_parse_error, start, p); 488 | // @TODO expand unicode escapes to UTF8 489 | *out++ = (char) n; 490 | } 491 | *out = 0; 492 | lexer->string = lexer->string_storage; 493 | lexer->string_len = (int) (out - lexer->string_storage); 494 | return stb__clex_token(lexer, type, start, p); 495 | } 496 | 497 | int stb_c_lexer_get_token(stb_lexer *lexer) 498 | { 499 | char *p = lexer->parse_point; 500 | 501 | // skip whitespace and comments 502 | for (;;) { 503 | #ifdef STB_C_LEX_ISWHITE 504 | while (p != lexer->stream_end) { 505 | int n; 506 | n = STB_C_LEX_ISWHITE(p); 507 | if (n == 0) break; 508 | if (lexer->eof && lexer->eof - lexer->parse_point < n) 509 | return stb__clex_token(tok, CLEX_parse_error, p,lexer->eof-1); 510 | p += n; 511 | } 512 | #else 513 | while (p != lexer->eof && stb__clex_iswhite(*p)) 514 | ++p; 515 | #endif 516 | 517 | STB_C_LEX_CPP_COMMENTS( 518 | if (p != lexer->eof && p[0] == '/' && p[1] == '/') { 519 | while (p != lexer->eof && *p != '\r' && *p != '\n') 520 | ++p; 521 | continue; 522 | } 523 | ) 524 | 525 | STB_C_LEX_C_COMMENTS( 526 | if (p != lexer->eof && p[0] == '/' && p[1] == '*') { 527 | char *start = p; 528 | p += 2; 529 | while (p != lexer->eof && (p[0] != '*' || p[1] != '/')) 530 | ++p; 531 | if (p == lexer->eof) 532 | return stb__clex_token(lexer, CLEX_parse_error, start, p-1); 533 | p += 2; 534 | continue; 535 | } 536 | ) 537 | 538 | #ifdef STB__clex_discard_preprocessor 539 | // @TODO this discards everything after a '#', regardless 540 | // of where in the line the # is, rather than requiring it 541 | // be at the start. (because this parser doesn't otherwise 542 | // check for line breaks!) 543 | if (p != lexer->eof && p[0] == '#') { 544 | while (p != lexer->eof && *p != '\r' && *p != '\n') 545 | ++p; 546 | continue; 547 | } 548 | #endif 549 | 550 | break; 551 | } 552 | 553 | if (p == lexer->eof) 554 | return stb__clex_eof(lexer); 555 | 556 | switch (*p) { 557 | default: 558 | if ( (*p >= 'a' && *p <= 'z') 559 | || (*p >= 'A' && *p <= 'Z') 560 | || *p == '_' || (unsigned char) *p >= 128 // >= 128 is UTF8 char 561 | STB_C_LEX_DOLLAR_IDENTIFIER( || *p == '$' ) ) 562 | { 563 | int n = 0; 564 | lexer->string = lexer->string_storage; 565 | lexer->string_len = n; 566 | do { 567 | if (n+1 >= lexer->string_storage_len) 568 | return stb__clex_token(lexer, CLEX_parse_error, p, p+n); 569 | lexer->string[n] = p[n]; 570 | ++n; 571 | } while ( 572 | (p[n] >= 'a' && p[n] <= 'z') 573 | || (p[n] >= 'A' && p[n] <= 'Z') 574 | || (p[n] >= '0' && p[n] <= '9') // allow digits in middle of identifier 575 | || p[n] == '_' || (unsigned char) p[n] >= 128 576 | STB_C_LEX_DOLLAR_IDENTIFIER( || p[n] == '$' ) 577 | ); 578 | lexer->string[n] = 0; 579 | return stb__clex_token(lexer, CLEX_id, p, p+n-1); 580 | } 581 | 582 | // check for EOF 583 | STB_C_LEX_0_IS_EOF( 584 | if (*p == 0) 585 | return stb__clex_eof(lexer); 586 | ) 587 | 588 | single_char: 589 | // not an identifier, return the character as itself 590 | return stb__clex_token(lexer, *p, p, p); 591 | 592 | case '+': 593 | if (p+1 != lexer->eof) { 594 | STB_C_LEX_C_INCREMENTS(if (p[1] == '+') return stb__clex_token(lexer, CLEX_plusplus, p,p+1);) 595 | STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_pluseq , p,p+1);) 596 | } 597 | goto single_char; 598 | case '-': 599 | if (p+1 != lexer->eof) { 600 | STB_C_LEX_C_INCREMENTS(if (p[1] == '-') return stb__clex_token(lexer, CLEX_minusminus, p,p+1);) 601 | STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_minuseq , p,p+1);) 602 | STB_C_LEX_C_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_arrow , p,p+1);) 603 | } 604 | goto single_char; 605 | case '&': 606 | if (p+1 != lexer->eof) { 607 | STB_C_LEX_C_LOGICAL( if (p[1] == '&') return stb__clex_token(lexer, CLEX_andand, p,p+1);) 608 | STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_andeq , p,p+1);) 609 | } 610 | goto single_char; 611 | case '|': 612 | if (p+1 != lexer->eof) { 613 | STB_C_LEX_C_LOGICAL( if (p[1] == '|') return stb__clex_token(lexer, CLEX_oror, p,p+1);) 614 | STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_oreq, p,p+1);) 615 | } 616 | goto single_char; 617 | case '=': 618 | if (p+1 != lexer->eof) { 619 | STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_eq, p,p+1);) 620 | STB_C_LEX_EQUAL_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_eqarrow, p,p+1);) 621 | } 622 | goto single_char; 623 | case '!': 624 | STB_C_LEX_C_COMPARISONS(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_noteq, p,p+1);) 625 | goto single_char; 626 | case '^': 627 | STB_C_LEX_C_BITWISEEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_xoreq, p,p+1)); 628 | goto single_char; 629 | case '%': 630 | STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_modeq, p,p+1)); 631 | goto single_char; 632 | case '*': 633 | STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_muleq, p,p+1)); 634 | goto single_char; 635 | case '/': 636 | STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_diveq, p,p+1)); 637 | goto single_char; 638 | case '<': 639 | if (p+1 != lexer->eof) { 640 | STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_lesseq, p,p+1);) 641 | STB_C_LEX_C_SHIFTS( if (p[1] == '<') { 642 | STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=') 643 | return stb__clex_token(lexer, CLEX_shleq, p,p+2);) 644 | return stb__clex_token(lexer, CLEX_shl, p,p+1); 645 | } 646 | ) 647 | } 648 | goto single_char; 649 | case '>': 650 | if (p+1 != lexer->eof) { 651 | STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_greatereq, p,p+1);) 652 | STB_C_LEX_C_SHIFTS( if (p[1] == '>') { 653 | STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=') 654 | return stb__clex_token(lexer, CLEX_shreq, p,p+2);) 655 | return stb__clex_token(lexer, CLEX_shr, p,p+1); 656 | } 657 | ) 658 | } 659 | goto single_char; 660 | 661 | case '"': 662 | STB_C_LEX_C_DQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_dqstring);) 663 | goto single_char; 664 | case '\'': 665 | STB_C_LEX_C_SQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_sqstring);) 666 | STB_C_LEX_C_CHARS( 667 | { 668 | char *start = p; 669 | lexer->int_number = stb__clex_parse_char(p+1, &p); 670 | if (lexer->int_number < 0) 671 | return stb__clex_token(lexer, CLEX_parse_error, start,start); 672 | if (p == lexer->eof || *p != '\'') 673 | return stb__clex_token(lexer, CLEX_parse_error, start,p); 674 | return stb__clex_token(lexer, CLEX_charlit, start, p+1); 675 | }) 676 | goto single_char; 677 | 678 | case '0': 679 | #if defined(STB__clex_hex_ints) || defined(STB__clex_hex_floats) 680 | if (p+1 != lexer->eof) { 681 | if (p[1] == 'x' || p[1] == 'X') { 682 | char *q; 683 | 684 | #ifdef STB__clex_hex_floats 685 | for (q=p+2; 686 | q != lexer->eof && ((*q >= '0' && *q <= '9') || (*q >= 'a' && *q <= 'f') || (*q >= 'A' && *q <= 'F')); 687 | ++q); 688 | if (q != lexer->eof) { 689 | if (*q == '.' STB_C_LEX_FLOAT_NO_DECIMAL(|| *q == 'p' || *q == 'P')) { 690 | #ifdef STB__CLEX_use_stdlib 691 | lexer->real_number = strtod((char *) p, (char**) &q); 692 | #else 693 | lexer->real_number = stb__clex_parse_float(p, &q); 694 | #endif 695 | 696 | if (p == q) 697 | return stb__clex_token(lexer, CLEX_parse_error, p,q); 698 | return stb__clex_parse_suffixes(lexer, CLEX_floatlit, p,q, STB_C_LEX_FLOAT_SUFFIXES); 699 | 700 | } 701 | } 702 | #endif // STB__CLEX_hex_floats 703 | 704 | #ifdef STB__clex_hex_ints 705 | #ifdef STB__CLEX_use_stdlib 706 | lexer->int_number = strtol((char *) p, (char **) &q, 16); 707 | #else 708 | { 709 | stb__clex_int n=0; 710 | for (q=p+2; q != lexer->eof; ++q) { 711 | if (*q >= '0' && *q <= '9') 712 | n = n*16 + (*q - '0'); 713 | else if (*q >= 'a' && *q <= 'f') 714 | n = n*16 + (*q - 'a') + 10; 715 | else if (*q >= 'A' && *q <= 'F') 716 | n = n*16 + (*q - 'A') + 10; 717 | else 718 | break; 719 | } 720 | lexer->int_number = n; 721 | } 722 | #endif 723 | if (q == p+2) 724 | return stb__clex_token(lexer, CLEX_parse_error, p-2,p-1); 725 | return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_HEX_SUFFIXES); 726 | #endif 727 | } 728 | } 729 | #endif // defined(STB__clex_hex_ints) || defined(STB__clex_hex_floats) 730 | // can't test for octal because we might parse '0.0' as float or as '0' '.' '0', 731 | // so have to do float first 732 | 733 | /* FALL THROUGH */ 734 | case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': 735 | 736 | #ifdef STB__clex_decimal_floats 737 | { 738 | char *q = p; 739 | while (q != lexer->eof && (*q >= '0' && *q <= '9')) 740 | ++q; 741 | if (q != lexer->eof) { 742 | if (*q == '.' STB_C_LEX_FLOAT_NO_DECIMAL(|| *q == 'e' || *q == 'E')) { 743 | #ifdef STB__CLEX_use_stdlib 744 | lexer->real_number = strtod((char *) p, (char**) &q); 745 | #else 746 | lexer->real_number = stb__clex_parse_float(p, &q); 747 | #endif 748 | 749 | return stb__clex_parse_suffixes(lexer, CLEX_floatlit, p,q, STB_C_LEX_FLOAT_SUFFIXES); 750 | 751 | } 752 | } 753 | } 754 | #endif // STB__clex_decimal_floats 755 | 756 | #ifdef STB__clex_octal_ints 757 | if (p[0] == '0') { 758 | char *q = p; 759 | #ifdef STB__CLEX_use_stdlib 760 | lexer->int_number = strtol((char *) p, (char **) &q, 8); 761 | #else 762 | stb__clex_int n=0; 763 | while (q != lexer->eof) { 764 | if (*q >= '0' && *q <= '7') 765 | n = n*8 + (*q - '0'); 766 | else 767 | break; 768 | ++q; 769 | } 770 | if (q != lexer->eof && (*q == '8' || *q=='9')) 771 | return stb__clex_token(lexer, CLEX_parse_error, p, q); 772 | lexer->int_number = n; 773 | #endif 774 | return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES); 775 | } 776 | #endif // STB__clex_octal_ints 777 | 778 | #ifdef STB__clex_decimal_ints 779 | { 780 | char *q = p; 781 | #ifdef STB__CLEX_use_stdlib 782 | lexer->int_number = strtol((char *) p, (char **) &q, 10); 783 | #else 784 | stb__clex_int n=0; 785 | while (q != lexer->eof) { 786 | if (*q >= '0' && *q <= '9') 787 | n = n*10 + (*q - '0'); 788 | else 789 | break; 790 | ++q; 791 | } 792 | lexer->int_number = n; 793 | #endif 794 | return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES); 795 | } 796 | #endif // STB__clex_decimal_ints 797 | goto single_char; 798 | } 799 | } 800 | 801 | void print_token(stb_lexer *lexer) 802 | { 803 | switch (lexer->token) { 804 | case CLEX_id : printf("_%s", lexer->string); break; 805 | case CLEX_eq : printf("=="); break; 806 | case CLEX_noteq : printf("!="); break; 807 | case CLEX_lesseq : printf("<="); break; 808 | case CLEX_greatereq : printf(">="); break; 809 | case CLEX_andand : printf("&&"); break; 810 | case CLEX_oror : printf("||"); break; 811 | case CLEX_shl : printf("<<"); break; 812 | case CLEX_shr : printf(">>"); break; 813 | case CLEX_plusplus : printf("++"); break; 814 | case CLEX_minusminus: printf("--"); break; 815 | case CLEX_arrow : printf("->"); break; 816 | case CLEX_andeq : printf("&="); break; 817 | case CLEX_oreq : printf("|="); break; 818 | case CLEX_xoreq : printf("^="); break; 819 | case CLEX_pluseq : printf("+="); break; 820 | case CLEX_minuseq : printf("-="); break; 821 | case CLEX_muleq : printf("*="); break; 822 | case CLEX_diveq : printf("/="); break; 823 | case CLEX_modeq : printf("%%="); break; 824 | case CLEX_shleq : printf("<<="); break; 825 | case CLEX_shreq : printf(">>="); break; 826 | case CLEX_eqarrow : printf("=>"); break; 827 | case CLEX_dqstring : printf("\"%s\"", lexer->string); break; 828 | case CLEX_sqstring : printf("'\"%s\"'", lexer->string); break; 829 | case CLEX_charlit : printf("'%s'", lexer->string); break; 830 | #if defined(STB__clex_int_as_double) && !defined(STB__CLEX_use_stdlib) 831 | case CLEX_intlit : printf("#%g", lexer->real_number); break; 832 | #else 833 | case CLEX_intlit : printf("#%ld", lexer->int_number); break; 834 | #endif 835 | case CLEX_floatlit : printf("%g", lexer->real_number); break; 836 | default: 837 | if (lexer->token >= 0 && lexer->token < 256) 838 | printf("%c", (int) lexer->token); 839 | else { 840 | printf("<<>>\n", lexer->token); 841 | } 842 | break; 843 | } 844 | } 845 | #endif // STB_C_LEXER_IMPLEMENTATION 846 | 847 | #ifdef STB_C_LEXER_SELF_TEST 848 | #define _CRT_SECURE_NO_WARNINGS 849 | #include 850 | #include 851 | 852 | /* Force a test 853 | of parsing 854 | multiline comments */ 855 | 856 | /*/ comment /*/ 857 | /**/ extern /**/ 858 | 859 | void dummy(void) 860 | { 861 | double some_floats[] = { 862 | 1.0501, -10.4e12, 5E+10, 863 | #if 0 // not supported in C++ or C-pre-99, so don't try to compile it, but let our parser test it 864 | 0x1.0p+24, 0xff.FP-8, 0x1p-23, 865 | #endif 866 | 4. 867 | }; 868 | (void) sizeof(some_floats); 869 | (void) some_floats[1]; 870 | 871 | printf("test %d",1); // https://github.com/nothings/stb/issues/13 872 | } 873 | 874 | int main(int argc, char **argv) 875 | { 876 | FILE *f = fopen("stb_c_lexer.h","rb"); 877 | char *text = (char *) malloc(1 << 20); 878 | int len = f ? (int) fread(text, 1, 1<<20, f) : -1; 879 | stb_lexer lex; 880 | if (len < 0) { 881 | fprintf(stderr, "Error opening file\n"); 882 | free(text); 883 | fclose(f); 884 | return 1; 885 | } 886 | fclose(f); 887 | 888 | stb_c_lexer_init(&lex, text, text+len, (char *) malloc(0x10000), 0x10000); 889 | while (stb_c_lexer_get_token(&lex)) { 890 | if (lex.token == CLEX_parse_error) { 891 | printf("\n<<>>\n"); 892 | break; 893 | } 894 | print_token(&lex); 895 | printf(" "); 896 | } 897 | return 0; 898 | } 899 | #endif 900 | /* 901 | ------------------------------------------------------------------------------ 902 | This software is available under 2 licenses -- choose whichever you prefer. 903 | ------------------------------------------------------------------------------ 904 | ALTERNATIVE A - MIT License 905 | Copyright (c) 2017 Sean Barrett 906 | Permission is hereby granted, free of charge, to any person obtaining a copy of 907 | this software and associated documentation files (the "Software"), to deal in 908 | the Software without restriction, including without limitation the rights to 909 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 910 | of the Software, and to permit persons to whom the Software is furnished to do 911 | so, subject to the following conditions: 912 | The above copyright notice and this permission notice shall be included in all 913 | copies or substantial portions of the Software. 914 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 915 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 916 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 917 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 918 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 919 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 920 | SOFTWARE. 921 | ------------------------------------------------------------------------------ 922 | ALTERNATIVE B - Public Domain (www.unlicense.org) 923 | This is free and unencumbered software released into the public domain. 924 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 925 | software, either in source code form or as a compiled binary, for any purpose, 926 | commercial or non-commercial, and by any means. 927 | In jurisdictions that recognize copyright laws, the author or authors of this 928 | software dedicate any and all copyright interest in the software to the public 929 | domain. We make this dedication for the benefit of the public at large and to 930 | the detriment of our heirs and successors. We intend this dedication to be an 931 | overt act of relinquishment in perpetuity of all present and future rights to 932 | this software under copyright law. 933 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 934 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 935 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 936 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 937 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 938 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 939 | ------------------------------------------------------------------------------ 940 | */ 941 | --------------------------------------------------------------------------------