|
|
1.1 ! root 1: /* ! 2: Hatari - options.c ! 3: ! 4: This file is distributed under the GNU Public License, version 2 or at ! 5: your option any later version. Read the file gpl.txt for details. ! 6: ! 7: Functions for showing and parsing all of Hatari's command line options. ! 8: ! 9: To add a new option: ! 10: - Add option ID to the enum ! 11: - Add the option information to HatariOptions[] ! 12: - Add required actions for that ID to switch in Opt_ParseParameters() ! 13: */ ! 14: const char Options_fileid[] = "Hatari options.c : " __DATE__ " " __TIME__; ! 15: ! 16: #include <ctype.h> ! 17: #include <stdio.h> ! 18: #include <stdlib.h> ! 19: #include <string.h> ! 20: #include <assert.h> ! 21: #include <SDL.h> ! 22: ! 23: #include "main.h" ! 24: #include "options.h" ! 25: #include "configuration.h" ! 26: #include "control.h" ! 27: #include "debugui.h" ! 28: #include "file.h" ! 29: #include "screen.h" ! 30: #include "video.h" ! 31: #include "log.h" ! 32: #include "avi_record.h" ! 33: ! 34: #include "hatari-glue.h" ! 35: ! 36: ! 37: bool bLoadAutoSave; /* Load autosave memory snapshot at startup */ ! 38: bool bLoadMemorySave; /* Load memory snapshot provided via option at startup */ ! 39: bool bBiosIntercept; /* whether UAE should intercept Bios & XBios calls */ ! 40: bool AviRecordOnStartup; /* Start avi recording at startup */ ! 41: ! 42: static bool bNoSDLParachute; ! 43: ! 44: /* List of supported options. */ ! 45: enum { ! 46: OPT_HEADER, /* options section header */ ! 47: OPT_HELP, /* general options */ ! 48: OPT_VERSION, ! 49: OPT_CONFIRMQUIT, ! 50: OPT_CONFIGFILE, ! 51: OPT_FASTFORWARD, ! 52: OPT_MONO, /* display options */ ! 53: OPT_MONITOR, ! 54: OPT_FULLSCREEN, ! 55: OPT_WINDOW, ! 56: OPT_GRAB, ! 57: OPT_ZOOM, ! 58: OPT_MAXWIDTH, ! 59: OPT_MAXHEIGHT, ! 60: OPT_ASPECT, ! 61: OPT_BORDERS, ! 62: OPT_FRAMESKIPS, ! 63: OPT_STATUSBAR, ! 64: OPT_DRIVE_LED, ! 65: OPT_SPEC512, ! 66: OPT_FORCEBPP, ! 67: OPT_VDI, /* VDI options */ ! 68: OPT_VDI_PLANES, ! 69: OPT_VDI_WIDTH, ! 70: OPT_VDI_HEIGHT, ! 71: OPT_AVIRECORD, /* record options */ ! 72: OPT_AVIRECORD_VCODEC, ! 73: OPT_AVIRECORD_FPS, ! 74: OPT_AVIRECORD_CROP, ! 75: OPT_AVIRECORD_FILE, ! 76: OPT_JOYSTICK, /* device options */ ! 77: OPT_JOYSTICK0, ! 78: OPT_JOYSTICK1, ! 79: OPT_JOYSTICK2, ! 80: OPT_JOYSTICK3, ! 81: OPT_JOYSTICK4, ! 82: OPT_JOYSTICK5, ! 83: OPT_PRINTER, ! 84: OPT_MIDI_IN, ! 85: OPT_MIDI_OUT, ! 86: OPT_RS232_IN, ! 87: OPT_RS232_OUT, ! 88: OPT_DISKA, /* disk options */ ! 89: OPT_DISKB, ! 90: OPT_SLOWFLOPPY, ! 91: OPT_WRITEPROT_FLOPPY, ! 92: OPT_WRITEPROT_HD, ! 93: OPT_HARDDRIVE, ! 94: OPT_ACSIHDIMAGE, ! 95: OPT_IDEMASTERHDIMAGE, ! 96: OPT_IDESLAVEHDIMAGE, ! 97: OPT_MEMSIZE, /* memory options */ ! 98: OPT_TOS, ! 99: OPT_CARTRIDGE, ! 100: OPT_MEMSTATE, ! 101: OPT_CPULEVEL, /* CPU options */ ! 102: OPT_CPUCLOCK, ! 103: OPT_COMPATIBLE, ! 104: OPT_MACHINE, /* system options */ ! 105: OPT_BLITTER, ! 106: OPT_TIMERD, ! 107: OPT_DSP, ! 108: OPT_SOUND, ! 109: OPT_SOUNDBUFFERSIZE, ! 110: OPT_KEYMAPFILE, ! 111: OPT_DEBUG, /* debug options */ ! 112: OPT_BIOSINTERCEPT, ! 113: OPT_TRACE, ! 114: OPT_TRACEFILE, ! 115: OPT_PARSE, ! 116: OPT_SAVECONFIG, ! 117: OPT_PARACHUTE, ! 118: OPT_CONTROLSOCKET, ! 119: OPT_LOGFILE, ! 120: OPT_LOGLEVEL, ! 121: OPT_ALERTLEVEL, ! 122: OPT_RUNVBLS, ! 123: OPT_ERROR, ! 124: OPT_CONTINUE ! 125: }; ! 126: ! 127: typedef struct { ! 128: unsigned int id; /* option ID */ ! 129: const char *chr; /* short option */ ! 130: const char *str; /* long option */ ! 131: const char *arg; /* type name for argument, if any */ ! 132: const char *desc; /* option description */ ! 133: } opt_t; ! 134: ! 135: /* it's easier to edit these if they are kept in the same order as the enums */ ! 136: static const opt_t HatariOptions[] = { ! 137: ! 138: { OPT_HEADER, NULL, NULL, NULL, "General" }, ! 139: { OPT_HELP, "-h", "--help", ! 140: NULL, "Print this help text and exit" }, ! 141: { OPT_VERSION, "-v", "--version", ! 142: NULL, "Print version number and exit" }, ! 143: { OPT_CONFIRMQUIT, NULL, "--confirm-quit", ! 144: "<bool>", "Whether Hatari confirms quit" }, ! 145: { OPT_CONFIGFILE, "-c", "--configfile", ! 146: "<file>", "Use <file> instead of the default hatari config file" }, ! 147: { OPT_FASTFORWARD, NULL, "--fast-forward", ! 148: "<bool>", "Help skipping stuff on fast machine" }, ! 149: ! 150: { OPT_HEADER, NULL, NULL, NULL, "Display" }, ! 151: { OPT_MONO, "-m", "--mono", ! 152: NULL, "Start in monochrome mode instead of color" }, ! 153: { OPT_MONITOR, NULL, "--monitor", ! 154: "<x>", "Select monitor type (x = mono/rgb/vga/tv)" }, ! 155: { OPT_FULLSCREEN,"-f", "--fullscreen", ! 156: NULL, "Start emulator in fullscreen mode" }, ! 157: { OPT_WINDOW, "-w", "--window", ! 158: NULL, "Start emulator in window mode" }, ! 159: { OPT_GRAB, NULL, "--grab", ! 160: NULL, "Grab mouse (also) in window mode" }, ! 161: { OPT_ZOOM, "-z", "--zoom", ! 162: "<x>", "Double small resolutions (1=no, 2=yes)" }, ! 163: { OPT_MAXWIDTH, NULL, "--max-width", ! 164: "<x>", "Maximum window width for zooming (Falcon/TT only)" }, ! 165: { OPT_MAXHEIGHT, NULL, "--max-height", ! 166: "<x>", "Maximum window height for zooming (Falcon/TT only)" }, ! 167: { OPT_ASPECT, NULL, "--aspect", ! 168: "<bool>", "Monitor aspect ratio correction (Falcon/TT only)" }, ! 169: { OPT_BORDERS, NULL, "--borders", ! 170: "<bool>", "Show ST/STE screen borders (for overscan demos etc)" }, ! 171: { OPT_FRAMESKIPS, NULL, "--frameskips", ! 172: "<x>", "Skip <x> frames after each shown frame (0=off, >4=auto/max)" }, ! 173: { OPT_STATUSBAR, NULL, "--statusbar", ! 174: "<bool>", "Show statusbar (floppy leds etc)" }, ! 175: { OPT_DRIVE_LED, NULL, "--drive-led", ! 176: "<bool>", "Show overlay drive led when statusbar isn't shown" }, ! 177: { OPT_SPEC512, NULL, "--spec512", ! 178: "<x>", "Spec512 palette threshold (0 <= x <= 512, 0=disable)" }, ! 179: { OPT_FORCEBPP, NULL, "--bpp", ! 180: "<x>", "Force internal bitdepth (x = 8/15/16/32, 0=disable)" }, ! 181: ! 182: { OPT_HEADER, NULL, NULL, NULL, "VDI" }, ! 183: { OPT_VDI, NULL, "--vdi", ! 184: "<bool>", "Whether to use VDI screen mode" }, ! 185: { OPT_VDI_PLANES,NULL, "--vdi-planes", ! 186: "<x>", "VDI mode bit-depth (x = 1/2/4)" }, ! 187: { OPT_VDI_WIDTH, NULL, "--vdi-width", ! 188: "<w>", "VDI mode width (320 < w <= 1280)" }, ! 189: { OPT_VDI_HEIGHT, NULL, "--vdi-height", ! 190: "<h>", "VDI mode height (200 < h <= 960)" }, ! 191: ! 192: { OPT_HEADER, NULL, NULL, NULL, "Video recording" }, ! 193: { OPT_AVIRECORD, NULL, "--avirecord", ! 194: NULL, "Start AVI recording" }, ! 195: { OPT_AVIRECORD_VCODEC, NULL, "--avi-vcodec", ! 196: "<x>", "Select avi video codec (x = bmp/png)" }, ! 197: { OPT_AVIRECORD_FPS, NULL, "--avi-fps", ! 198: "<x>", "Force avi frame rate (x = 50/60/71/...)" }, ! 199: { OPT_AVIRECORD_CROP, NULL, "--avi-crop", ! 200: "<bool>", "Remove status bar from the recorded file" }, ! 201: { OPT_AVIRECORD_FILE, NULL, "--avi-file", ! 202: "<file>", "Use <file> to record avi" }, ! 203: ! 204: { OPT_HEADER, NULL, NULL, NULL, "Devices" }, ! 205: { OPT_JOYSTICK, "-j", "--joystick", ! 206: "<port>", "Emulate joystick with cursor keys in given port (0-5)" }, ! 207: /* these have to be exactly the same as I'm relying compiler giving ! 208: * them the same same string pointer when strings are identical ! 209: * (Opt_ShowHelpSection() skips successive options with same help ! 210: * pointer). ! 211: */ ! 212: { OPT_JOYSTICK0, NULL, "--joy<port>", ! 213: "<type>", "Set joystick type (none/keys/real) for given port" }, ! 214: { OPT_JOYSTICK1, NULL, "--joy<port>", ! 215: "<type>", "Set joystick type (none/keys/real) for given port" }, ! 216: { OPT_JOYSTICK2, NULL, "--joy<port>", ! 217: "<type>", "Set joystick type (none/keys/real) for given port" }, ! 218: { OPT_JOYSTICK3, NULL, "--joy<port>", ! 219: "<type>", "Set joystick type (none/keys/real) for given port" }, ! 220: { OPT_JOYSTICK4, NULL, "--joy<port>", ! 221: "<type>", "Set joystick type (none/keys/real) for given port" }, ! 222: { OPT_JOYSTICK5, NULL, "--joy<port>", ! 223: "<type>", "Set joystick type (none/keys/real) for given port" }, ! 224: { OPT_PRINTER, NULL, "--printer", ! 225: "<file>", "Enable printer support and write data to <file>" }, ! 226: { OPT_MIDI_IN, NULL, "--midi-in", ! 227: "<file>", "Enable MIDI and use <file> as the input device" }, ! 228: { OPT_MIDI_OUT, NULL, "--midi-out", ! 229: "<file>", "Enable MIDI and use <file> as the output device" }, ! 230: { OPT_RS232_IN, NULL, "--rs232-in", ! 231: "<file>", "Enable serial port and use <file> as the input device" }, ! 232: { OPT_RS232_OUT, NULL, "--rs232-out", ! 233: "<file>", "Enable serial port and use <file> as the output device" }, ! 234: ! 235: { OPT_HEADER, NULL, NULL, NULL, "Disk" }, ! 236: { OPT_DISKA, NULL, "--disk-a", ! 237: "<file>", "Set disk image for floppy drive A" }, ! 238: { OPT_DISKB, NULL, "--disk-b", ! 239: "<file>", "Set disk image for floppy drive B" }, ! 240: { OPT_SLOWFLOPPY, NULL, "--slowfdc", ! 241: "<bool>", "Slow down floppy disk access emulation" }, ! 242: { OPT_WRITEPROT_FLOPPY, NULL, "--protect-floppy", ! 243: "<x>", "Write protect floppy image contents (on/off/auto)" }, ! 244: { OPT_WRITEPROT_HD, NULL, "--protect-hd", ! 245: "<x>", "Write protect harddrive <dir> contents (on/off/auto)" }, ! 246: { OPT_HARDDRIVE, "-d", "--harddrive", ! 247: "<dir>", "Emulate harddrive partition(s) with <dir> contents" }, ! 248: { OPT_ACSIHDIMAGE, NULL, "--acsi", ! 249: "<file>", "Emulate an ACSI harddrive with an image <file>" }, ! 250: { OPT_IDEMASTERHDIMAGE, NULL, "--ide-master", ! 251: "<file>", "Emulate an IDE master harddrive with an image <file>" }, ! 252: { OPT_IDESLAVEHDIMAGE, NULL, "--ide-slave", ! 253: "<file>", "Emulate an IDE slave harddrive with an image <file>" }, ! 254: ! 255: { OPT_HEADER, NULL, NULL, NULL, "Memory" }, ! 256: { OPT_MEMSIZE, "-s", "--memsize", ! 257: "<x>", "ST RAM size (x = size in MiB from 0 to 14, 0 = 512KiB)" }, ! 258: { OPT_TOS, "-t", "--tos", ! 259: "<file>", "Use TOS image <file>" }, ! 260: { OPT_CARTRIDGE, NULL, "--cartridge", ! 261: "<file>", "Use ROM cartridge image <file>" }, ! 262: { OPT_MEMSTATE, NULL, "--memstate", ! 263: "<file>", "Load memory snap-shot <file>" }, ! 264: ! 265: { OPT_HEADER, NULL, NULL, NULL, "CPU" }, ! 266: { OPT_CPULEVEL, NULL, "--cpulevel", ! 267: "<x>", "Set the CPU type (x => 680x0) (EmuTOS/TOS 2.06 only!)" }, ! 268: { OPT_CPUCLOCK, NULL, "--cpuclock", ! 269: "<x>", "Set the CPU clock (x = 8/16/32)" }, ! 270: { OPT_COMPATIBLE, NULL, "--compatible", ! 271: "<bool>", "Use a more compatible (but slower) 68000 CPU mode" }, ! 272: ! 273: { OPT_HEADER, NULL, NULL, NULL, "Misc system" }, ! 274: { OPT_MACHINE, NULL, "--machine", ! 275: "<x>", "Select machine type (x = st/ste/tt/falcon)" }, ! 276: { OPT_BLITTER, NULL, "--blitter", ! 277: "<bool>", "Use blitter emulation (ST only)" }, ! 278: { OPT_TIMERD, NULL, "--timer-d", ! 279: "<bool>", "Patch Timer-D (about doubles Hatari speed)" }, ! 280: { OPT_DSP, NULL, "--dsp", ! 281: "<x>", "DSP emulation (x = none/dummy/emu, Falcon only)" }, ! 282: { OPT_SOUND, NULL, "--sound", ! 283: "<x>", "Sound frequency (x=off/6000-50066, off=fastest)" }, ! 284: { OPT_SOUNDBUFFERSIZE, NULL, "--sound-buffer-size", ! 285: "<x>", "Sound buffer size for SDL in ms (x=0/10-100, 0=use default buffer size)" }, ! 286: { OPT_KEYMAPFILE, "-k", "--keymap", ! 287: "<file>", "Read (additional) keyboard mappings from <file>" }, ! 288: ! 289: { OPT_HEADER, NULL, NULL, NULL, "Debug" }, ! 290: { OPT_DEBUG, "-D", "--debug", ! 291: NULL, "Toggle whether CPU exceptions invoke debugger" }, ! 292: { OPT_BIOSINTERCEPT, NULL, "--bios-intercept", ! 293: NULL, "Enable Bios/XBios interception (experimental)" }, ! 294: { OPT_TRACE, NULL, "--trace", ! 295: "<trace1,...>", "Activate emulation tracing, see '--trace help'" }, ! 296: { OPT_TRACEFILE, NULL, "--trace-file", ! 297: "<file>", "Save trace output to <file> (default=stderr)" }, ! 298: { OPT_PARSE, NULL, "--parse", ! 299: "<file>", "Parse/execute debugger commands from <file>" }, ! 300: { OPT_SAVECONFIG, NULL, "--saveconfig", ! 301: NULL, "Save current Hatari configuration and exit" }, ! 302: { OPT_PARACHUTE, NULL, "--no-parachute", ! 303: NULL, "Disable SDL parachute to get Hatari core dumps" }, ! 304: #if HAVE_UNIX_DOMAIN_SOCKETS ! 305: { OPT_CONTROLSOCKET, NULL, "--control-socket", ! 306: "<file>", "Hatari reads options from given socket at run-time" }, ! 307: #endif ! 308: { OPT_LOGFILE, NULL, "--log-file", ! 309: "<file>", "Save log output to <file> (default=stderr)" }, ! 310: { OPT_LOGLEVEL, NULL, "--log-level", ! 311: "<x>", "Log output level (x=debug/todo/info/warn/error/fatal)" }, ! 312: { OPT_ALERTLEVEL, NULL, "--alert-level", ! 313: "<x>", "Show dialog for log messages above given level" }, ! 314: { OPT_RUNVBLS, NULL, "--run-vbls", ! 315: "<x>", "Exit after x VBLs" }, ! 316: ! 317: { OPT_ERROR, NULL, NULL, NULL, NULL } ! 318: }; ! 319: ! 320: ! 321: /** ! 322: * Show version string and license. ! 323: */ ! 324: static void Opt_ShowVersion(void) ! 325: { ! 326: printf("\n" PROG_NAME ! 327: " - the Atari ST, STE, TT and Falcon emulator.\n\n"); ! 328: printf("Hatari is free software licensed under the GNU General" ! 329: " Public License.\n\n"); ! 330: } ! 331: ! 332: ! 333: /** ! 334: * Calculate option + value len ! 335: */ ! 336: static unsigned int Opt_OptionLen(const opt_t *opt) ! 337: { ! 338: unsigned int len; ! 339: len = strlen(opt->str); ! 340: if (opt->arg) ! 341: { ! 342: len += strlen(opt->arg); ! 343: len += 1; ! 344: /* with arg, short options go to another line */ ! 345: } ! 346: else ! 347: { ! 348: if (opt->chr) ! 349: { ! 350: /* ' or -c' */ ! 351: len += 6; ! 352: } ! 353: } ! 354: return len; ! 355: } ! 356: ! 357: ! 358: /** ! 359: * Show single option ! 360: */ ! 361: static void Opt_ShowOption(const opt_t *opt, unsigned int maxlen) ! 362: { ! 363: char buf[64]; ! 364: if (!maxlen) ! 365: { ! 366: maxlen = Opt_OptionLen(opt); ! 367: } ! 368: assert(maxlen < sizeof(buf)); ! 369: if (opt->arg) ! 370: { ! 371: sprintf(buf, "%s %s", opt->str, opt->arg); ! 372: printf(" %-*s %s\n", maxlen, buf, opt->desc); ! 373: if (opt->chr) ! 374: { ! 375: printf(" or %s %s\n", opt->chr, opt->arg); ! 376: } ! 377: } ! 378: else ! 379: { ! 380: if (opt->chr) ! 381: { ! 382: sprintf(buf, "%s or %s", opt->str, opt->chr); ! 383: printf(" %-*s %s\n", maxlen, buf, opt->desc); ! 384: } ! 385: else ! 386: { ! 387: printf(" %-*s %s\n", maxlen, opt->str, opt->desc); ! 388: } ! 389: } ! 390: } ! 391: ! 392: /** ! 393: * Show options for section starting from 'start_opt', ! 394: * return next option after this section. ! 395: */ ! 396: static const opt_t *Opt_ShowHelpSection(const opt_t *start_opt) ! 397: { ! 398: const opt_t *opt, *last; ! 399: unsigned int len, maxlen = 0; ! 400: const char *previous = NULL; ! 401: ! 402: /* find longest option name and check option IDs */ ! 403: for (opt = start_opt; opt->id != OPT_HEADER && opt->id != OPT_ERROR; opt++) ! 404: { ! 405: len = Opt_OptionLen(opt); ! 406: if (len > maxlen) ! 407: { ! 408: maxlen = len; ! 409: } ! 410: } ! 411: last = opt; ! 412: ! 413: /* output all options */ ! 414: for (opt = start_opt; opt != last; opt++) ! 415: { ! 416: if (previous != opt->str) ! 417: { ! 418: Opt_ShowOption(opt, maxlen); ! 419: } ! 420: previous = opt->str; ! 421: } ! 422: return last; ! 423: } ! 424: ! 425: ! 426: /** ! 427: * Show help text. ! 428: */ ! 429: static void Opt_ShowHelp(void) ! 430: { ! 431: const opt_t *opt = HatariOptions; ! 432: ! 433: Opt_ShowVersion(); ! 434: printf("Usage:\n hatari [options] [disk image name]\n"); ! 435: ! 436: while(opt->id != OPT_ERROR) ! 437: { ! 438: if (opt->id == OPT_HEADER) ! 439: { ! 440: assert(opt->desc); ! 441: printf("\n%s options:\n", opt->desc); ! 442: opt++; ! 443: } ! 444: opt = Opt_ShowHelpSection(opt); ! 445: } ! 446: printf("\nSpecial option values:\n"); ! 447: printf("<bool>\tDisable by using 'n', 'no', 'off', 'false', or '0'\n"); ! 448: printf("\tEnable by using 'y', 'yes', 'on', 'true' or '1'\n"); ! 449: printf("<file>\tDevices accept also special 'stdout' and 'stderr' file names\n"); ! 450: printf("\t(if you use stdout for midi or printer, set log to stderr).\n"); ! 451: printf("\tSetting the file to 'none', disables given device or disk\n"); ! 452: } ! 453: ! 454: ! 455: /** ! 456: * Show Hatari version and usage. ! 457: * If 'error' given, show that error message. ! 458: * If 'optid' != OPT_ERROR, tells for which option the error is, ! 459: * otherwise 'value' is show as the option user gave. ! 460: * Return false if error string was given, otherwise true ! 461: */ ! 462: static bool Opt_ShowError(unsigned int optid, const char *value, const char *error) ! 463: { ! 464: const opt_t *opt; ! 465: ! 466: Opt_ShowVersion(); ! 467: printf("Usage:\n hatari [options] [disk image name]\n\n" ! 468: "Try option \"-h\" or \"--help\" to display more information.\n"); ! 469: ! 470: if (error) ! 471: { ! 472: if (optid == OPT_ERROR) ! 473: { ! 474: fprintf(stderr, "\nError: %s (%s)\n", error, value); ! 475: } ! 476: else ! 477: { ! 478: for (opt = HatariOptions; opt->id != OPT_ERROR; opt++) ! 479: { ! 480: if (optid == opt->id) ! 481: break; ! 482: } ! 483: if (value != NULL) ! 484: { ! 485: fprintf(stderr, ! 486: "\nError while parsing argument \"%s\" for option \"%s\":\n" ! 487: " %s\n", value, opt->str, error); ! 488: } ! 489: else ! 490: { ! 491: fprintf(stderr, "\nError (%s): %s\n", opt->str, error); ! 492: } ! 493: fprintf(stderr, "\nOption usage:\n"); ! 494: Opt_ShowOption(opt, 0); ! 495: } ! 496: return false; ! 497: } ! 498: return true; ! 499: } ! 500: ! 501: ! 502: /** ! 503: * If 'conf' given, set it: ! 504: * - true if given option 'arg' is y/yes/on/true/1 ! 505: * - false if given option 'arg' is n/no/off/false/0 ! 506: * Return false for any other value, otherwise true ! 507: */ ! 508: static bool Opt_Bool(const char *arg, int optid, bool *conf) ! 509: { ! 510: const char *enablers[] = { "y", "yes", "on", "true", "1", NULL }; ! 511: const char *disablers[] = { "n", "no", "off", "false", "0", NULL }; ! 512: const char **bool_str, *orig = arg; ! 513: char *input, *str; ! 514: ! 515: input = strdup(arg); ! 516: str = input; ! 517: while (*str) ! 518: { ! 519: *str++ = tolower(*arg++); ! 520: } ! 521: for (bool_str = enablers; *bool_str; bool_str++) ! 522: { ! 523: if (strcmp(input, *bool_str) == 0) ! 524: { ! 525: free(input); ! 526: if (conf) ! 527: { ! 528: *conf = true; ! 529: } ! 530: return true; ! 531: } ! 532: } ! 533: for (bool_str = disablers; *bool_str; bool_str++) ! 534: { ! 535: if (strcmp(input, *bool_str) == 0) ! 536: { ! 537: free(input); ! 538: if (conf) ! 539: { ! 540: *conf = false; ! 541: } ! 542: return true; ! 543: } ! 544: } ! 545: free(input); ! 546: return Opt_ShowError(optid, orig, "Not a <bool> value"); ! 547: } ! 548: ! 549: ! 550: /** ! 551: * checks str argument agaist options of type "--option<digit>". ! 552: * If match is found, returns ID for that, otherwise OPT_CONTINUE ! 553: * and OPT_ERROR for errors. ! 554: */ ! 555: static int Opt_CheckBracketValue(const opt_t *opt, const char *str) ! 556: { ! 557: const char *bracket, *optstr; ! 558: size_t offset; ! 559: int digit, i; ! 560: ! 561: if (!opt->str) ! 562: { ! 563: return OPT_CONTINUE; ! 564: } ! 565: bracket = strchr(opt->str, '<'); ! 566: if (!bracket) ! 567: { ! 568: return OPT_CONTINUE; ! 569: } ! 570: offset = bracket - opt->str; ! 571: if (strncmp(opt->str, str, offset) != 0) ! 572: { ! 573: return OPT_CONTINUE; ! 574: } ! 575: digit = str[offset] - '0'; ! 576: if (digit < 0 || digit > 9) ! 577: { ! 578: return OPT_CONTINUE; ! 579: } ! 580: optstr = opt->str; ! 581: for (i = 0; opt->str == optstr; opt++, i++) ! 582: { ! 583: if (i == digit) ! 584: { ! 585: return opt->id; ! 586: } ! 587: } ! 588: /* fprintf(stderr, "opt: %s (%d), str: %s (%d), digit: %d\n", ! 589: opt->str, offset+1, str, strlen(str), digit); ! 590: */ ! 591: return OPT_ERROR; ! 592: } ! 593: ! 594: ! 595: /** ! 596: * matches string under given index in the argv against all Hatari ! 597: * short and long options. If match is found, returns ID for that, ! 598: * otherwise shows help and returns OPT_ERROR. ! 599: * ! 600: * Checks also that if option is supposed to have argument, ! 601: * whether there's one. ! 602: */ ! 603: static int Opt_WhichOption(int argc, const char *argv[], int idx) ! 604: { ! 605: const opt_t *opt; ! 606: const char *str = argv[idx]; ! 607: int id; ! 608: ! 609: for (opt = HatariOptions; opt->id != OPT_ERROR; opt++) ! 610: { ! 611: /* exact option name matches? */ ! 612: if (!((opt->str && !strcmp(str, opt->str)) || ! 613: (opt->chr && !strcmp(str, opt->chr)))) ! 614: { ! 615: /* no, maybe name<digit> matches? */ ! 616: id = Opt_CheckBracketValue(opt, str); ! 617: if (id == OPT_CONTINUE) ! 618: { ! 619: continue; ! 620: } ! 621: if (id == OPT_ERROR) ! 622: { ! 623: break; ! 624: } ! 625: } ! 626: /* matched, check args */ ! 627: if (opt->arg) ! 628: { ! 629: if (idx+1 >= argc) ! 630: { ! 631: Opt_ShowError(opt->id, NULL, "Missing argument"); ! 632: return OPT_ERROR; ! 633: } ! 634: /* early check for bools */ ! 635: if (strcmp(opt->arg, "<bool>") == 0) ! 636: { ! 637: if (!Opt_Bool(argv[idx+1], opt->id, NULL)) ! 638: { ! 639: return OPT_ERROR; ! 640: } ! 641: } ! 642: } ! 643: return opt->id; ! 644: } ! 645: Opt_ShowError(OPT_ERROR, argv[idx], "Unrecognized option"); ! 646: return OPT_ERROR; ! 647: } ! 648: ! 649: ! 650: /** ! 651: * If 'checkexits' is true, assume 'src' is a file and check whether it ! 652: * exists before copying 'src' to 'dst'. Otherwise just copy option src ! 653: * string to dst. ! 654: * If a pointer to (bool) 'option' is given, set that option to true. ! 655: * - However, if src is "none", leave dst unmodified & set option to false. ! 656: * ("none" is used to disable options related to file arguments) ! 657: * Return false if there were errors, otherwise true ! 658: */ ! 659: static bool Opt_StrCpy(int optid, bool checkexist, char *dst, const char *src, size_t dstlen, bool *option) ! 660: { ! 661: if (option) ! 662: { ! 663: *option = false; ! 664: if(strcasecmp(src, "none") == 0) ! 665: { ! 666: return true; ! 667: } ! 668: } ! 669: if (strlen(src) >= dstlen) ! 670: { ! 671: return Opt_ShowError(optid, src, "File name too long!"); ! 672: } ! 673: if (checkexist && !File_Exists(src)) ! 674: { ! 675: return Opt_ShowError(optid, src, "Given file doesn't exist (or has wrong file permissions)!"); ! 676: } ! 677: if (option) ! 678: { ! 679: *option = true; ! 680: } ! 681: strcpy(dst, src); ! 682: return true; ! 683: } ! 684: ! 685: ! 686: /** ! 687: * Return SDL_INIT_NOPARACHUTE flag if user requested SDL parachute ! 688: * to be disabled to get proper Hatari core dumps. By default returns ! 689: * zero so that SDL parachute will be used to restore video mode on ! 690: * unclean Hatari termination. ! 691: */ ! 692: Uint32 Opt_GetNoParachuteFlag(void) ! 693: { ! 694: if (bNoSDLParachute) { ! 695: return SDL_INIT_NOPARACHUTE; ! 696: } ! 697: return 0; ! 698: } ! 699: ! 700: ! 701: /** ! 702: * parse all Hatari command line options and set Hatari state accordingly. ! 703: * Returns true if everything was OK, false otherwise. ! 704: */ ! 705: bool Opt_ParseParameters(int argc, const char *argv[]) ! 706: { ! 707: int ncpu, skips, zoom, planes, cpuclock, threshold, memsize, port, freq, temp; ! 708: const char *errstr; ! 709: int i, ok = true; ! 710: int val; ! 711: ! 712: /* Defaults for loading initial memory snap-shots */ ! 713: bLoadMemorySave = false; ! 714: bLoadAutoSave = ConfigureParams.Memory.bAutoSave; ! 715: ! 716: for(i = 1; i < argc; i++) ! 717: { ! 718: if (argv[i][0] != '-') ! 719: { ! 720: continue; ! 721: } ! 722: ! 723: /* WhichOption() checks also that there is an argument, ! 724: * so we don't need to check that below ! 725: */ ! 726: switch(Opt_WhichOption(argc, argv, i)) ! 727: { ! 728: ! 729: /* general options */ ! 730: case OPT_HELP: ! 731: Opt_ShowHelp(); ! 732: return false; ! 733: ! 734: case OPT_VERSION: ! 735: Opt_ShowVersion(); ! 736: return false; ! 737: ! 738: case OPT_CONFIRMQUIT: ! 739: ok = Opt_Bool(argv[++i], OPT_CONFIRMQUIT, &ConfigureParams.Log.bConfirmQuit); ! 740: break; ! 741: ! 742: case OPT_FASTFORWARD: ! 743: ok = Opt_Bool(argv[++i], OPT_FASTFORWARD, &ConfigureParams.System.bFastForward); ! 744: break; ! 745: ! 746: case OPT_CONFIGFILE: ! 747: i += 1; ! 748: /* true -> file needs to exist */ ! 749: ok = Opt_StrCpy(OPT_CONFIGFILE, true, sConfigFileName, ! 750: argv[i], sizeof(sConfigFileName), NULL); ! 751: if (ok) ! 752: { ! 753: Configuration_Load(NULL); ! 754: bLoadAutoSave = ConfigureParams.Memory.bAutoSave; ! 755: } ! 756: break; ! 757: ! 758: /* display options */ ! 759: case OPT_MONO: ! 760: ConfigureParams.Screen.nMonitorType = MONITOR_TYPE_MONO; ! 761: bLoadAutoSave = false; ! 762: break; ! 763: ! 764: case OPT_MONITOR: ! 765: i += 1; ! 766: if (strcasecmp(argv[i], "mono") == 0) ! 767: { ! 768: ConfigureParams.Screen.nMonitorType = MONITOR_TYPE_MONO; ! 769: } ! 770: else if (strcasecmp(argv[i], "rgb") == 0) ! 771: { ! 772: ConfigureParams.Screen.nMonitorType = MONITOR_TYPE_RGB; ! 773: } ! 774: else if (strcasecmp(argv[i], "vga") == 0) ! 775: { ! 776: ConfigureParams.Screen.nMonitorType = MONITOR_TYPE_VGA; ! 777: } ! 778: else if (strcasecmp(argv[i], "tv") == 0) ! 779: { ! 780: ConfigureParams.Screen.nMonitorType = MONITOR_TYPE_TV; ! 781: } ! 782: else ! 783: { ! 784: return Opt_ShowError(OPT_MONITOR, argv[i], "Unknown monitor type"); ! 785: } ! 786: bLoadAutoSave = false; ! 787: break; ! 788: ! 789: case OPT_FULLSCREEN: ! 790: ConfigureParams.Screen.bFullScreen = true; ! 791: break; ! 792: ! 793: case OPT_WINDOW: ! 794: ConfigureParams.Screen.bFullScreen = false; ! 795: break; ! 796: ! 797: case OPT_GRAB: ! 798: bGrabMouse = true; ! 799: break; ! 800: ! 801: case OPT_ZOOM: ! 802: zoom = atoi(argv[++i]); ! 803: if (zoom < 1) ! 804: { ! 805: return Opt_ShowError(OPT_ZOOM, argv[i], "Invalid zoom value"); ! 806: } ! 807: if (zoom > 1) ! 808: { ! 809: ConfigureParams.Screen.nMaxWidth = 1024; ! 810: ConfigureParams.Screen.nMaxHeight = 768; ! 811: } ! 812: else ! 813: { ! 814: ConfigureParams.Screen.nMaxWidth = 1024; ! 815: ConfigureParams.Screen.nMaxHeight = 768; ! 816: } ! 817: break; ! 818: ! 819: case OPT_MAXWIDTH: ! 820: ConfigureParams.Screen.nMaxWidth = atoi(argv[++i]); ! 821: break; ! 822: ! 823: case OPT_MAXHEIGHT: ! 824: ConfigureParams.Screen.nMaxHeight = atoi(argv[++i]); ! 825: break; ! 826: ! 827: case OPT_ASPECT: ! 828: ok = Opt_Bool(argv[++i], OPT_ASPECT, &ConfigureParams.Screen.bAspectCorrect); ! 829: break; ! 830: ! 831: case OPT_FRAMESKIPS: ! 832: skips = atoi(argv[++i]); ! 833: if (skips < 0) ! 834: { ! 835: return Opt_ShowError(OPT_FRAMESKIPS, argv[i], ! 836: "Invalid frame skip value"); ! 837: } ! 838: else if (skips > 8) ! 839: { ! 840: Log_Printf(LOG_WARN, "Extravagant frame skip value %d!\n", skips); ! 841: } ! 842: ConfigureParams.Screen.nFrameSkips = skips; ! 843: break; ! 844: ! 845: case OPT_BORDERS: ! 846: ok = Opt_Bool(argv[++i], OPT_BORDERS, &ConfigureParams.Screen.bAllowOverscan); ! 847: break; ! 848: ! 849: case OPT_STATUSBAR: ! 850: ok = Opt_Bool(argv[++i], OPT_STATUSBAR, &ConfigureParams.Screen.bShowStatusbar); ! 851: break; ! 852: ! 853: case OPT_DRIVE_LED: ! 854: ok = Opt_Bool(argv[++i], OPT_DRIVE_LED, &ConfigureParams.Screen.bShowDriveLed); ! 855: break; ! 856: ! 857: case OPT_SPEC512: ! 858: threshold = atoi(argv[++i]); ! 859: if (threshold < 0 || threshold > 512) ! 860: { ! 861: return Opt_ShowError(OPT_SPEC512, argv[i], ! 862: "Invalid palette writes per line threshold for Spec512"); ! 863: } ! 864: ConfigureParams.Screen.nSpec512Threshold = threshold; ! 865: break; ! 866: ! 867: case OPT_FORCEBPP: ! 868: planes = atoi(argv[++i]); ! 869: switch(planes) ! 870: { ! 871: case 32: ! 872: case 16: ! 873: case 15: ! 874: case 8: ! 875: break; /* supported */ ! 876: case 24: ! 877: planes = 32; /* We do not support 24 bpp (yet) */ ! 878: break; ! 879: default: ! 880: return Opt_ShowError(OPT_FORCEBPP, argv[i], "Invalid bit depth"); ! 881: } ! 882: ConfigureParams.Screen.nForceBpp = planes; ! 883: break; ! 884: ! 885: /* avi recording options */ ! 886: case OPT_AVIRECORD: ! 887: AviRecordOnStartup = true; ! 888: break; ! 889: ! 890: case OPT_AVIRECORD_VCODEC: ! 891: i += 1; ! 892: if (strcasecmp(argv[i], "bmp") == 0) ! 893: { ! 894: // AviRecordDefaultVcodec = AVI_RECORD_VIDEO_CODEC_BMP; ! 895: } ! 896: else if (strcasecmp(argv[i], "png") == 0) ! 897: { ! 898: // AviRecordDefaultVcodec = AVI_RECORD_VIDEO_CODEC_PNG; ! 899: } ! 900: else ! 901: { ! 902: return Opt_ShowError(OPT_AVIRECORD_VCODEC, argv[i], "Unknown video codec"); ! 903: } ! 904: break; ! 905: ! 906: case OPT_AVIRECORD_FPS: ! 907: val = atoi(argv[++i]); ! 908: if (val < 0 || val > 100) ! 909: { ! 910: return Opt_ShowError(OPT_AVIRECORD_FPS, argv[i], ! 911: "Invalid frame rate for avi recording"); ! 912: } ! 913: // AviRecordDefaultFps = val; ! 914: break; ! 915: ! 916: case OPT_AVIRECORD_CROP: ! 917: // ok = Opt_Bool(argv[++i], OPT_AVIRECORD_CROP, &AviRecordDefaultCrop); ! 918: break; ! 919: ! 920: case OPT_AVIRECORD_FILE: ! 921: i += 1; ! 922: /* false -> file is created if it doesn't exist */ ! 923: // ok = Opt_StrCpy(OPT_AVIRECORD_FILE, false, AviRecordFile, ! 924: // argv[i], sizeof(AviRecordFile), NULL); ! 925: break; ! 926: ! 927: ! 928: /* Memory options */ ! 929: case OPT_MEMSIZE: ! 930: memsize = atoi(argv[++i]); ! 931: if (memsize < 0 || memsize > 14) ! 932: { ! 933: return Opt_ShowError(OPT_MEMSIZE, argv[i], "Invalid memory size"); ! 934: } ! 935: ConfigureParams.Memory.nMemorySize = memsize; ! 936: bLoadAutoSave = false; ! 937: break; ! 938: ! 939: case OPT_TOS: ! 940: i += 1; ! 941: ok = Opt_StrCpy(OPT_TOS, true, ConfigureParams.Rom.szTosImageFileName, ! 942: argv[i], sizeof(ConfigureParams.Rom.szTosImageFileName), ! 943: NULL); ! 944: if (ok) ! 945: { ! 946: bLoadAutoSave = false; ! 947: } ! 948: break; ! 949: ! 950: case OPT_CARTRIDGE: ! 951: i += 1; ! 952: ok = Opt_StrCpy(OPT_CARTRIDGE, true, ConfigureParams.Rom.szCartridgeImageFileName, ! 953: argv[i], sizeof(ConfigureParams.Rom.szCartridgeImageFileName), ! 954: NULL); ! 955: if (ok) ! 956: { ! 957: bLoadAutoSave = false; ! 958: } ! 959: break; ! 960: ! 961: case OPT_MEMSTATE: ! 962: i += 1; ! 963: ok = Opt_StrCpy(OPT_MEMSTATE, true, ConfigureParams.Memory.szMemoryCaptureFileName, ! 964: argv[i], sizeof(ConfigureParams.Memory.szMemoryCaptureFileName), ! 965: NULL); ! 966: if (ok) ! 967: { ! 968: bLoadMemorySave = true; ! 969: bLoadAutoSave = false; ! 970: } ! 971: break; ! 972: ! 973: /* CPU options */ ! 974: case OPT_CPULEVEL: ! 975: /* UAE core uses cpu_level variable */ ! 976: ncpu = atoi(argv[++i]); ! 977: if(ncpu < 0 || ncpu > 4) ! 978: { ! 979: return Opt_ShowError(OPT_CPULEVEL, argv[i], "Invalid CPU level"); ! 980: } ! 981: ConfigureParams.System.nCpuLevel = ncpu; ! 982: bLoadAutoSave = false; ! 983: break; ! 984: ! 985: case OPT_CPUCLOCK: ! 986: cpuclock = atoi(argv[++i]); ! 987: if(cpuclock != 8 && cpuclock != 16 && cpuclock != 32) ! 988: { ! 989: return Opt_ShowError(OPT_CPUCLOCK, argv[i], "Invalid CPU clock"); ! 990: } ! 991: ConfigureParams.System.nCpuFreq = cpuclock; ! 992: bLoadAutoSave = false; ! 993: break; ! 994: ! 995: case OPT_COMPATIBLE: ! 996: ok = Opt_Bool(argv[++i], OPT_COMPATIBLE, &ConfigureParams.System.bCompatibleCpu); ! 997: if (ok) ! 998: { ! 999: bLoadAutoSave = false; ! 1000: } ! 1001: break; ! 1002: ! 1003: /* system options */ ! 1004: case OPT_MACHINE: ! 1005: i += 1; ! 1006: if (strcasecmp(argv[i], "st") == 0) ! 1007: { ! 1008: ConfigureParams.System.nMachineType = MACHINE_ST; ! 1009: ConfigureParams.System.nCpuLevel = 0; ! 1010: ConfigureParams.System.nCpuFreq = 8; ! 1011: } ! 1012: else if (strcasecmp(argv[i], "ste") == 0) ! 1013: { ! 1014: ConfigureParams.System.nMachineType = MACHINE_STE; ! 1015: ConfigureParams.System.nCpuLevel = 0; ! 1016: ConfigureParams.System.nCpuFreq = 8; ! 1017: } ! 1018: else if (strcasecmp(argv[i], "tt") == 0) ! 1019: { ! 1020: ConfigureParams.System.nMachineType = MACHINE_TT; ! 1021: ConfigureParams.System.nCpuLevel = 3; ! 1022: ConfigureParams.System.nCpuFreq = 32; ! 1023: } ! 1024: else if (strcasecmp(argv[i], "falcon") == 0) ! 1025: { ! 1026: ConfigureParams.System.nMachineType = MACHINE_FALCON; ! 1027: ConfigureParams.System.nCpuLevel = 3; ! 1028: ConfigureParams.System.nCpuFreq = 16; ! 1029: } ! 1030: else ! 1031: { ! 1032: return Opt_ShowError(OPT_MACHINE, argv[i], "Unknown machine type"); ! 1033: } ! 1034: bLoadAutoSave = false; ! 1035: break; ! 1036: ! 1037: ! 1038: case OPT_TIMERD: ! 1039: ok = Opt_Bool(argv[++i], OPT_TIMERD, &ConfigureParams.System.bPatchTimerD); ! 1040: break; ! 1041: ! 1042: ! 1043: case OPT_SOUNDBUFFERSIZE: ! 1044: i += 1; ! 1045: temp = atoi(argv[i]); ! 1046: if ( temp == 0 ) /* use default setting for SDL */ ! 1047: ; ! 1048: else if (temp < 10 || temp > 100) ! 1049: { ! 1050: return Opt_ShowError(OPT_SOUNDBUFFERSIZE, argv[i], "Unsupported sound buffer size"); ! 1051: } ! 1052: ConfigureParams.Sound.SdlAudioBufferSize = temp; ! 1053: break; ! 1054: ! 1055: case OPT_KEYMAPFILE: ! 1056: i += 1; ! 1057: ok = Opt_StrCpy(OPT_KEYMAPFILE, true, ConfigureParams.Keyboard.szMappingFileName, ! 1058: argv[i], sizeof(ConfigureParams.Keyboard.szMappingFileName), ! 1059: NULL); ! 1060: if (ok) ! 1061: { ! 1062: ConfigureParams.Keyboard.nKeymapType = KEYMAP_LOADED; ! 1063: } ! 1064: break; ! 1065: ! 1066: /* debug options */ ! 1067: case OPT_DEBUG: ! 1068: if (bExceptionDebugging) ! 1069: { ! 1070: fprintf(stderr, "Exception debugging disabled.\n"); ! 1071: bExceptionDebugging = false; ! 1072: } ! 1073: else ! 1074: { ! 1075: fprintf(stderr, "Exception debugging enabled.\n"); ! 1076: bExceptionDebugging = true; ! 1077: } ! 1078: break; ! 1079: ! 1080: case OPT_PARACHUTE: ! 1081: bNoSDLParachute = true; ! 1082: break; ! 1083: ! 1084: ! 1085: case OPT_TRACE: ! 1086: i += 1; ! 1087: errstr = Log_SetTraceOptions(argv[i]); ! 1088: if (errstr) ! 1089: { ! 1090: if (!errstr[0]) { ! 1091: /* silent parsing termination */ ! 1092: return false; ! 1093: } ! 1094: return Opt_ShowError(OPT_TRACE, argv[i], errstr); ! 1095: } ! 1096: break; ! 1097: ! 1098: case OPT_TRACEFILE: ! 1099: i += 1; ! 1100: ok = Opt_StrCpy(OPT_TRACEFILE, false, ConfigureParams.Log.sTraceFileName, ! 1101: argv[i], sizeof(ConfigureParams.Log.sTraceFileName), ! 1102: NULL); ! 1103: break; ! 1104: ! 1105: case OPT_CONTROLSOCKET: ! 1106: i += 1; ! 1107: errstr = Control_SetSocket(argv[i]); ! 1108: if (errstr) ! 1109: { ! 1110: return Opt_ShowError(OPT_CONTROLSOCKET, argv[i], errstr); ! 1111: } ! 1112: break; ! 1113: ! 1114: case OPT_LOGFILE: ! 1115: i += 1; ! 1116: ok = Opt_StrCpy(OPT_LOGFILE, false, ConfigureParams.Log.sLogFileName, ! 1117: argv[i], sizeof(ConfigureParams.Log.sLogFileName), ! 1118: NULL); ! 1119: break; ! 1120: ! 1121: case OPT_PARSE: ! 1122: i += 1; ! 1123: ok = DebugUI_SetParseFile(argv[i]); ! 1124: break; ! 1125: ! 1126: case OPT_SAVECONFIG: ! 1127: /* Hatari-UI needs Hatari config to start */ ! 1128: Configuration_Save(); ! 1129: exit(0); ! 1130: break; ! 1131: ! 1132: case OPT_LOGLEVEL: ! 1133: i += 1; ! 1134: ConfigureParams.Log.nTextLogLevel = Log_ParseOptions(argv[i]); ! 1135: if (ConfigureParams.Log.nTextLogLevel == LOG_NONE) ! 1136: { ! 1137: return Opt_ShowError(OPT_LOGLEVEL, argv[i], "Unknown log level!"); ! 1138: } ! 1139: break; ! 1140: ! 1141: case OPT_ALERTLEVEL: ! 1142: i += 1; ! 1143: ConfigureParams.Log.nAlertDlgLogLevel = Log_ParseOptions(argv[i]); ! 1144: if (ConfigureParams.Log.nAlertDlgLogLevel == LOG_NONE) ! 1145: { ! 1146: return Opt_ShowError(OPT_ALERTLEVEL, argv[i], "Unknown alert level!"); ! 1147: } ! 1148: break; ! 1149: ! 1150: case OPT_RUNVBLS: ! 1151: nRunVBLs = atol(argv[++i]); ! 1152: break; ! 1153: ! 1154: case OPT_ERROR: ! 1155: /* unknown option or missing option parameter */ ! 1156: return false; ! 1157: ! 1158: default: ! 1159: return Opt_ShowError(OPT_ERROR, argv[i], "Internal Hatari error, unhandled option"); ! 1160: } ! 1161: if (!ok) ! 1162: { ! 1163: /* Opt_Bool() or Opt_StrCpy() failed */ ! 1164: return false; ! 1165: } ! 1166: } ! 1167: ! 1168: return true; ! 1169: } ! 1170: ! 1171: /** ! 1172: * Readline match callback for option name completion. ! 1173: * STATE = 0 -> different text from previous one. ! 1174: * Return next match or NULL if no matches. ! 1175: */ ! 1176: char *Opt_MatchOption(const char *text, int state) ! 1177: { ! 1178: static int i, len; ! 1179: const char *name; ! 1180: ! 1181: if (!state) { ! 1182: /* first match */ ! 1183: len = strlen(text); ! 1184: i = 0; ! 1185: } ! 1186: /* next match */ ! 1187: while (i < ARRAYSIZE(HatariOptions)) { ! 1188: name = HatariOptions[i++].str; ! 1189: if (name && strncasecmp(name, text, len) == 0) ! 1190: return (strdup(name)); ! 1191: } ! 1192: return NULL; ! 1193: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.