diff --git a/fleg/flag.odin b/fleg/flag.odin new file mode 100644 index 0000000..076917b --- /dev/null +++ b/fleg/flag.odin @@ -0,0 +1,162 @@ +#+vet explicit-allocators +package fleg + +import "base:runtime" +import "core:fmt" +import "core:os" +import "core:strconv" +import "core:strings" + +FORCE_HELP_ON_EMPTY_ARGS := false + +Flag_Value_Ptr :: union { + ^bool, + ^int, + ^string, + ^f32, + ^f64, +} + +Flag :: struct { + name: string, + value: Flag_Value_Ptr, + usage: string, + parsed: bool, // sck: flag to check if it has been parsed +} + +all_flags: [dynamic]Flag +flag_allocator: runtime.Allocator + +// sck: Used to create a customer allocator +init_custom_allocator :: proc(allocator: runtime.Allocator) { + destroy() // sck: make sure to destory the old allocations + + flag_allocator = allocator + all_flags = make([dynamic]Flag, flag_allocator) +} + +destroy :: proc() { + delete(all_flags) + all_flags = {} // sck: needed? +} + +print_flags :: proc() { + for f in all_flags { + fmt.printfln("%s: %v: %s", f.name, f.value, f.usage) + } +} + +print_usage :: proc() { + fmt.println("\tUsage: ") + for f in all_flags { + switch v in f.value { + case ^bool: + fmt.printfln("\t-%s:\n\t\t%s (default: %v)\n", f.name, f.usage, v^) + case ^int: + fmt.printfln("\t-%s:\n\t\t%s (default: %d)\n", f.name, f.usage, v^) + case ^string: + fmt.printfln("\t-%s:\n\t\t%s (default: %s)\n", f.name, f.usage, v^) + case ^f32: + fmt.printfln("\t-%s:\n\t\t%s (default: %f)\n", f.name, f.usage, v^) + case ^f64: + fmt.printfln("\t-%s:\n\t\t%s (default: %f)\n", f.name, f.usage, v^) + } + } + os.exit(0) +} + +BoolVar :: proc(ptr: ^bool, name: string, default: bool, usage: string) { + ptr^ = default + append(&all_flags, Flag{name = name, value = ptr, usage = usage}) +} + +IntVar :: proc(ptr: ^int, name: string, default: int, usage: string) { + ptr^ = default + append(&all_flags, Flag{name = name, value = ptr, usage = usage}) +} + +StringVar :: proc(ptr: ^string, name: string, default: string, usage: string) { + ptr^ = default + append(&all_flags, Flag{name = name, value = ptr, usage = usage}) +} + +Float32Var :: proc(ptr: ^f32, name: string, default: f32, usage: string) { + ptr^ = default + append(&all_flags, Flag{name = name, value = ptr, usage = usage}) +} + +Float64Var :: proc(ptr: ^f64, name: string, default: f64, usage: string) { + ptr^ = default + append(&all_flags, Flag{name = name, value = ptr, usage = usage}) +} + +parse_flags :: proc() { + if len(os.args) < 2 && FORCE_HELP_ON_EMPTY_ARGS {print_usage()} + + for &a in os.args { + if a == "-help" || a == "--help" {print_usage()} + if a == os.args[0] {continue} + + for &f in all_flags { + if f.parsed {continue} + a = strings.trim_prefix(a, "-") + + name := a + value: string + + split := strings.index_any(a, "=") + if split >= 0 { + name = a[:split] + value = a[split + 1:] + } else { + name = a + } + + if name == f.name && !f.parsed { + f.parsed = true + switch &v in f.value { + case ^bool: + if value == "" {break} + parsed, ok := strconv.parse_bool(value) + if !ok { + fmt.fprintfln(os.stderr, "[ERROR] Failed to parse flag %s: got %T, wanted bool", f.name, value) + os.exit(1) + } + v^ = parsed + + case ^int: + if value == "" {break} + parsed, ok := strconv.parse_int(value) + if !ok { + fmt.fprintfln(os.stderr, "[ERROR] Failed to parse flag %s: got %T, wanted int", f.name, value) + os.exit(1) + } + v^ = parsed + + case ^string: + if value == "" {break} + // TODO: validate string? + v^ = value + + case ^f32: + if value == "" {break} + parsed, ok := strconv.parse_f32(value) + if !ok { + fmt.fprintfln(os.stderr, "[ERROR] Failed to parse flag %s: got %T, wanted f32", f.name, value) + os.exit(1) + } + v^ = parsed + + case ^f64: + if value == "" {break} + parsed, ok := strconv.parse_f64(value) + if !ok { + fmt.fprintfln(os.stderr, "[ERROR] Failed to parse flag %s: got %T, wanted f64", f.name, value) + os.exit(1) + } + v^ = parsed + } + } + } + } +} diff --git a/main.odin b/main.odin index 65cb780..a1ad720 100644 --- a/main.odin +++ b/main.odin @@ -3,6 +3,7 @@ package main import "core:fmt" import "core:os" +import "fleg" import SDL "vendor:sdl2" WINDOW_TITLE :: "PPM Viewer" @@ -18,14 +19,23 @@ print_usage :: proc() { } main :: proc() { + /* if len(os.args) > 3 || len(os.args) < 2 { print_usage() } + */ file := Ppm_file{} - filename := os.args[1] - if ok, err := load_ppm(&file, filename, context.temp_allocator); !ok { - fmt.eprintfln("ERROR: Failed to load file '%s': %s ", filename, os.error_string(err)) + + file_arg: string + fleg.StringVar(&file_arg, "f", "in.ppm", "input file [.ppm]") + fleg.FORCE_HELP_ON_EMPTY_ARGS = true + defer fleg.destroy() + + fleg.parse_flags() + + if ok, err := load_ppm(&file, file_arg, context.temp_allocator); !ok { + fmt.eprintfln("ERROR: Failed to load file '%s': %s ", file_arg, os.error_string(err)) os.exit(1) } ppm_parse(&file, context.temp_allocator) diff --git a/ppm-viewer b/ppm-viewer index e07663d..c3d0de6 100755 Binary files a/ppm-viewer and b/ppm-viewer differ