/* * Copyright (C) 2026 Peter Mikkelsen * * This file is part of aplwc. * * Aplwc is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Aplwc is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with aplwc. If not, see . */ #define _POSIX_C_SOURCE 200809L #include #include #include #include "aplwc_repl.h" static unsigned long failsim_start; static unsigned long failsim_count; static void * my_alloc(size_t size, size_t *actual) { size_t target = size; void *p; if(actual){ if(NEVER(target == 0)) target = 1; else{ target--; target |= target >> 1; target |= target >> 2; target |= target >> 4; target |= target >> 8; target |= target >> 16; target |= target >> 32; target++; } } p = FAILCASE(malloc(target), NULL); if((p == NULL) && (size < target)){ target = size; p = FAILCASE(malloc(target), NULL); } if(actual) *actual = target; return p; } static void my_dealloc(void *p) { free(p); } static unsigned long my_setup_failsim(enum aplwc_failsim_parameter p, unsigned long def) { switch(p){ case APLWC_FAILSIM_START: return failsim_start = getenv_ulong("APLWC_SIMULATE_FAILURE_START", def); case APLWC_FAILSIM_COUNT: return failsim_count = getenv_ulong("APLWC_SIMULATE_FAILURE_COUNT", def); default: return def; } } int portable_main(int argc, char *argv[]) { struct aplwc_fns fns; struct aplwc *aplwc; enum aplwc_rc ret; AplwcLine line; AplwcValue value; char *input, *src; int line_number; size_t input_size, src_size, i; int opt, xflag, input_file; long tmp; FILE *f; unsigned long failsim_count, failsim_fails, failsim_strict; failsim_strict = getenv_ulong("APLWC_FAILSIM_STRICT", 0); aplwc_clear_line(&line); aplwc_clear_value(&value); input = NULL; xflag = 0; input_file = 0; line_number = 0; aplwc = NULL; memset(&fns, 0, sizeof(fns)); fns.alloc = my_alloc; fns.dealloc = my_dealloc; fns.setup_failsim = my_setup_failsim; while((opt = getopt(argc, argv, "x")) != -1){ switch(opt){ case 'x': xflag = 1; break; default: usage: fprintf(stderr, "Usage: %s [-x] [file]\n", argv[0]); ret = APLWC_OTHER_FAILURE; goto end; } } if((optind+1) == argc){ input_file = 1; f = fopen(argv[optind], "r"); if(f == NULL){ fprintf(stderr, "Could not open file: %s: %s\n", argv[optind], strerror(errno)); ret = APLWC_OTHER_FAILURE; goto end; } }else if(optind == argc) f = stdin; else goto usage; aplwc = aplwc_new(fns, &ret); if(ret){ fprintf(stderr, "Couldn't create new aplwc instance: %s\n", aplwc_errstr_rc(ret)); goto end; } if(input_file){ if(FAILCASE(fseek(f, 0, SEEK_END), -1)){ file_size: fprintf(stderr, "Failed to determine file size\n"); ret = APLWC_OTHER_FAILURE; goto end; } tmp = FAILCASE(ftell(f), -1); if(tmp < 0) goto file_size; input_size = tmp; rewind(f); input = FAILCASE(malloc(input_size), NULL); if(input == NULL){ fprintf(stderr, "Failed to allocate space for file contents\n"); ret = APLWC_OTHER_FAILURE; goto end; } if(FAILCASE(fread(input, 1, input_size, f), 0) < input_size){ fprintf(stderr, "Failed to read file contents\n"); ret = APLWC_OTHER_FAILURE; goto end; } fclose(f); src = input; src_size = input_size; if((input_size > 2) && (input[0] == '#') && (input[1] == '!')){ for(i = 0; i < input_size; i++){ if(input[i] == '\n'){ src += i; src_size -= i; xflag = 1; break; } } } ret = aplwc_load_module(aplwc, "Main", src, src_size); if(ret){ fprintf(stderr, "Couldn't load Main module from file %s: %s\n", argv[optind], aplwc_errstr_rc(ret)); goto end; } free(input); input = NULL; } if(xflag) goto end; for(;;){ if(input) free(input); if(aplwc_valid_line(&line)) aplwc_free_line(&line); if(aplwc_valid_value(&value)) aplwc_free_value(&value); input = NULL; if(getline(&input, &input_size, stdin) < 0){ free(input); input = NULL; break; }else{ line_number++; aplwc_read(aplwc, input, &line); if(!aplwc_valid_line(&line)){ line_error: fprintf(stderr, "error on line %d: %s: %s\n", line_number, aplwc_errstr(aplwc), input); if(failsim_strict){ ret = aplwc_last_error(aplwc); goto end; }else continue; } aplwc_eval(&line, &value); if(!aplwc_valid_value(&value)) goto line_error; fprintf(stdout, "%s\n", aplwc_value_string(&value)); } } end: if(input) free(input); if(aplwc_valid_line(&line)) aplwc_free_line(&line); if(NEVER(aplwc_valid_value(&value))) aplwc_free_value(&value); if(aplwc) aplwc_destroy(aplwc); failsim_count = aplwc_failsim_query(APLWC_FAILSIM_COUNT); failsim_fails = aplwc_failsim_query(APLWC_FAILSIM_FAILS); if(failsim_fails > 0){ switch(ret){ case APLWC_OK: case APLWC_OTHER_FAILURE: case APLWC_OUT_OF_MEMORY: break; default: if(failsim_strict) return 97; break; } if(failsim_count > failsim_fails) return 98; else return 99; } return (ret != APLWC_OK); }