private void parseOptions(HeadlessOptions options, String[] args, int startIndex, URL ghidraURL,
			List<File> filesToImport) throws InvalidInputException {

		String loaderName = null;
		List<Pair<String, String>> loaderArgs = new LinkedList<>();
		String languageId = null;
		String compilerSpecId = null;
		String keystorePath = null;
		String userId = null;
		boolean allowPasswordPrompt = false;
		List<Pair<String, String[]>> preScripts = new LinkedList<>();
		List<Pair<String, String[]>> postScripts = new LinkedList<>();

		for (int argi = startIndex; argi < args.length; argi++) {

			String arg = args[argi];
			if (checkArgument(Arg.LOG, args, argi)) {
				// Already processed
				argi++;
			}
			else if (checkArgument(Arg.SCRIPT_LOG, args, argi)) {
				// Already processed
				argi++;
			}
			else if (checkArgument(Arg.OVERWRITE, args, argi)) {
				options.enableOverwriteOnConflict(true);
			}
			else if (checkArgument(Arg.NO_ANALYSIS, args, argi)) {
				options.enableAnalysis(false);
			}
			else if (checkArgument(Arg.DELETE_PROJECT, args, argi)) {
				options.setDeleteCreatedProjectOnClose(true);
			}
			else if (checkArgument(Arg.LOADER, args, argi)) {
				loaderName = args[++argi];
			}
			else if (checkArgument(Arg.LOADER_ARGS, args, argi)) {
				if (isExistingArg(args[argi + 1])) {
					throw new InvalidInputException(args[argi] + " expects value to follow.");
				}
				loaderArgs.add(new Pair<>(arg, args[++argi]));
			}
			else if (checkArgument(Arg.PROCESSOR, args, argi)) {
				languageId = args[++argi];
			}
			else if (checkArgument(Arg.CSPEC, args, argi)) {
				compilerSpecId = args[++argi];
			}
			else if (checkArgument(Arg.PRE_SCRIPT, args, argi)) {
				String scriptName = args[++argi];
				String[] scriptArgs = getSubArguments(args, argi);
				argi += scriptArgs.length;
				preScripts.add(new Pair<>(scriptName, scriptArgs));
			}
			else if (checkArgument(Arg.POST_SCRIPT, args, argi)) {
				String scriptName = args[++argi];
				String[] scriptArgs = getSubArguments(args, argi);
				argi += scriptArgs.length;
				postScripts.add(new Pair<>(scriptName, scriptArgs));
			}
			else if (checkArgument(Arg.SCRIPT_PATH, args, argi)) {
				options.setScriptDirectories(args[++argi]);
			}
			else if (checkArgument(Arg.PROPERTIES_PATH, args, argi)) {
				options.setPropertiesFileDirectories(args[++argi]);
			}
			else if (checkArgument(Arg.IMPORT, args, argi)) {
				File inputFile = null;
				try {
					inputFile = new File(args[++argi]);
					inputFile = inputFile.getCanonicalFile();
				}
				catch (IOException e) {
					throw new InvalidInputException(
						"Failed to get canonical form of: " + inputFile);
				}
				if (!inputFile.isDirectory() && !inputFile.isFile()) {
					throw new InvalidInputException(
						inputFile + " is not a valid directory or file.");
				}

				HeadlessAnalyzer.checkValidFilename(inputFile.toString());

				filesToImport.add(inputFile);

				// Keep checking for OS-expanded files
				String nextArg;

				while (argi < (args.length - 1)) {
					nextArg = args[++argi];

					// Check if next argument is a parameter
					if (isExistingArg(nextArg)) {
						argi--;
						break;
					}

					File otherFile = new File(nextArg).getAbsoluteFile();
					if (!otherFile.isFile() && !otherFile.isDirectory()) {
						throw new InvalidInputException(
							otherFile + " is not a valid directory or file.");
					}

					HeadlessAnalyzer.checkValidFilename(otherFile.toString());

					filesToImport.add(otherFile);
				}
			}
			else if (checkArgument(Arg.CONNECT, args, argi)) {
				if ((argi + 1) < args.length) {
					arg = args[argi + 1];
					if (!isExistingArg(arg)) {
						// serverUID is optional argument after -connect
						userId = arg;
						++argi;
					}
				}
			}
			else if (checkArgument(Arg.COMMIT, args, argi)) {
				String comment = null;
				if ((argi + 1) < args.length) {
					arg = args[argi + 1];
					if (!isExistingArg(arg)) {
						// commit is optional argument after -commit
						comment = arg;
						++argi;
					}
				}
				options.setCommitFiles(true, comment);
			}
			else if (checkArgument(Arg.KEYSTORE, args, argi)) {
				keystorePath = args[++argi];
				File keystore = new File(keystorePath);
				if (!keystore.isFile()) {
					throw new InvalidInputException(
						keystore.getAbsolutePath() + " is not a valid keystore file.");
				}
			}
			else if (checkArgument(Arg.PASSWORD, args, argi)) {
				allowPasswordPrompt = true;
			}
			else if (checkArgument(Arg.ANALYSIS_TIMEOUT_PER_FILE, args, argi)) {
				options.setPerFileAnalysisTimeout(args[++argi]);
			}
			else if (checkArgument(Arg.PROCESS, args, argi)) {
				if (options.runScriptsNoImport) {
					throw new InvalidInputException(
						"The -process option may only be specified once.");
				}
				String processBinary = null;
				if ((argi + 1) < args.length) {
					arg = args[argi + 1];
					if (!isExistingArg(arg)) {
						// processBinary is optional argument after -process
						processBinary = arg;
						++argi;
					}
				}
				options.setRunScriptsNoImport(true, processBinary);
			}
			else if (checkArgument(Arg.RECURSIVE, args, argi)) {
				Integer depth = null;
				if ((argi + 1) < args.length) {
					arg = args[argi + 1];
					if (!isExistingArg(arg)) {
						// depth is optional argument after -recursive
						try {
							depth = Integer.parseInt(arg);
						}
						catch (NumberFormatException e) {
							throw new InvalidInputException("Invalid recursion depth: " + depth);
						}
						++argi;
					}
				}
				options.enableRecursiveProcessing(true, depth);
			}
			else if (checkArgument(Arg.READ_ONLY, args, argi)) {
				options.enableReadOnlyProcessing(true);
			}
			else if (checkArgument(Arg.MAX_CPU, args, argi)) {
				String cpuVal = args[++argi];
				try {
					options.setMaxCpu(Integer.parseInt(cpuVal));
				}
				catch (NumberFormatException nfe) {
					throw new InvalidInputException("Invalid value for max-cpu: " + cpuVal);
				}
			}
			else if (checkArgument(Arg.OK_TO_DELETE, args, argi)) {
				options.setOkToDelete(true);
			}
			else if (checkArgument(Arg.LIBRARY_SEARCH_PATHS, args, argi)) {
				LibrarySearchPathManager.setLibraryPaths(args[++argi].split(";"));
			}
			else if (isExistingArg(args[argi])) {
				throw new AssertionError("Valid option was not processed: " + args[argi]);
			}
			else {
				throw new InvalidInputException("Bad argument: " + arg);
			}
		}

		// Set up pre and post scripts
		options.setPreScriptsWithArgs(preScripts);
		options.setPostScriptsWithArgs(postScripts);

		// Set loader and loader args
		options.setLoader(loaderName, loaderArgs);

		// Set user-specified language and compiler spec
		options.setLanguageAndCompiler(languageId, compilerSpecId);

		// Set up optional Ghidra Server authenticator
		try {
			options.setClientCredentials(userId, keystorePath, allowPasswordPrompt);
		}
		catch (IOException e) {
			throw new InvalidInputException(
				"Failed to install Ghidra Server authenticator: " + e.getMessage());
		}

		// If -process was specified, inputFiles must be null or inputFiles.size must be 0.
		// Otherwise when not in -process mode, inputFiles can be null or inputFiles.size can be 0,
		// only if there are scripts to be run.
		if (options.runScriptsNoImport) {

			if (filesToImport != null && filesToImport.size() > 0) {
				System.err.print("Must use either -process or -import parameters, but not both.");
				System.err.print(" -process runs scripts over existing program(s) in a project, " +
					"whereas -import");
				System.err.println(" imports new programs and runs scripts and/or analyzes them " +
					"after import.");
				System.exit(EXIT_CODE_ERROR);
			}

			if (options.overwrite) {
				Msg.warn(HeadlessAnalyzer.class,
					"The -overwrite parameter does not apply to -process mode.  Ignoring overwrite " +
						"and continuing.");
			}

			if (options.readOnly && options.okToDelete) {
				System.err.println("You have specified the conflicting parameters -readOnly and " +
					"-okToDelete. Please pick one and try again.");
				System.exit(EXIT_CODE_ERROR);
			}
		}
		else {
			if (filesToImport == null || filesToImport.size() == 0) {
				if (options.preScripts.isEmpty() && options.postScripts.isEmpty()) {
					System.err.println("Nothing to do ... must specify -import, -process, or " +
						"prescript and/or postscript.");
					System.exit(EXIT_CODE_ERROR);
				}
				else {
					Msg.warn(HeadlessAnalyzer.class,
						"Neither the -import parameter nor the -process parameter was specified; " +
							"therefore, the specified prescripts and/or postscripts will be " +
							"executed without any type of program context.");
				}
			}
		}

		if (options.commit) {
			if (options.readOnly) {
				System.err.println("Can not use -commit and -readOnly at the same time.");
				System.exit(EXIT_CODE_ERROR);
			}
		}

		// Implied commit, only if not in process mode
		if (!options.commit && ghidraURL != null) {
			if (!options.readOnly) {
				// implied commit
				options.setCommitFiles(true, null);
			}
			else {
				Msg.warn(HeadlessAnalyzer.class,
					"-readOnly mode is on: for -process, changes will not be saved.");
			}
		}
	}
Parses the command line arguments and uses them to set the headless options.

@param options The headless options to set.
@param args The command line arguments to parse.
@param startIndex The index into the args array of where to start parsing.
@param ghidraURL The ghidra server url to connect to, or null if not using a url.
@param filesToImport A list to put files to import into.
@throws InvalidInputException if an error occurred parsing the arguments or setting
        the options.
Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/AnalyzeHeadless.java