public static CParseResults parseHeaderFiles(DataTypeManager[] openDTmanagers,
			String[] filenames, String[] includePaths,
			String[] args, DataTypeManager dtMgr, TaskMonitor monitor)
			throws ghidra.app.util.cparser.C.ParseException,
			ghidra.app.util.cparser.CPP.ParseException {

		String cppMessages = "";
		PreProcessor cpp = new PreProcessor();

		cpp.setArgs(args);
		cpp.addIncludePaths(includePaths);

		PrintStream os = System.out;

		String fName = dtMgr.getName();

		// make a path to tmpdir with name of data type manager
		String path = new File(Application.getUserTempDirectory(), fName).getAbsolutePath();
		// if file data type manager, use path to .gdt file
		if (dtMgr instanceof FileDataTypeManager) {
			path = ((FileDataTypeManager) dtMgr).getPath();
		}
		path = path + "_CParser.out";

		try {
			os = new PrintStream(new FileOutputStream(path));
		}
		catch (FileNotFoundException e2) {
			Msg.error(CParserUtils.class, "Unexpected Exception: " + e2.getMessage(), e2);
		}
		// cpp.setOutputStream(os);
		PrintStream old = System.out;
		System.setOut(os);

		cpp.setMonitor(monitor);
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		cpp.setOutputStream(bos);

		boolean parseSucceeded = false;
		try {
			for (String filename : filenames) {
				if (monitor.isCancelled()) {
					break;
				}
				if (filename.trim().startsWith("#")) {
					continue;
				}
				File file = new File(filename);
				// process each header file in the directory
				if (file.isDirectory()) {
					String[] children = file.list();
					if (children == null) {
						continue;
					}
					for (String element : children) {
						File child = new File(file.getAbsolutePath() + "/" + element);
						if (child.getName().endsWith(".h")) {
							parseFile(child.getAbsolutePath(), monitor, cpp);
						}
					}
				}
				else {
					parseFile(filename, monitor, cpp);
				}
			}
			parseSucceeded = true;
		}
		catch (Throwable e) {
			Msg.info(cpp, cpp.getParseMessages());
		}
		finally {
			System.out.println(bos);
			os.flush();
			os.close();
			System.setOut(old);

		}

		cppMessages = cpp.getParseMessages();
		if (!parseSucceeded) {
			return new CParseResults(cpp, cppMessages, "", false);
		}

		// process all the defines and add any that are integer values into
		// the Equates table
		cpp.getDefinitions().populateDefineEquates(openDTmanagers, dtMgr);

		String parserMessages = "";
		boolean cparseSucceeded = false;
		if (!monitor.isCancelled()) {
			monitor.setMessage("Parsing C");

			CParser cParser = new CParser(dtMgr, true, openDTmanagers);
			ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
			try {
				parserMessages = "";
				cParser.setParseFileName(fName);
				cParser.setMonitor(monitor);
				cParser.parse(bis);
				cparseSucceeded = cParser.didParseSucceed();
			}
			catch (RuntimeException re) {
				Msg.info(cpp, cpp.getParseMessages());
			}
			finally {
				parserMessages = cParser.getParseMessages();
			}
		}
		
		return new CParseResults(cpp, cppMessages, parserMessages, cparseSucceeded);
	}
Parse a set of C Header files and associated parsing arguments, data types are added to the provided
DTMgr.

Note: Using another open archive while parsing will cause:
- a dependence on the other archive
- any missing data types while parsing are supplied if present from an openDTMgr
- after parsing all data types parsed with an equivalent data type in any openDTMgr
    replaced by the data type from the openDTMgr
    
NOTE: This will only occur if the data type from the openDTMgr's is equivalent.

NOTE: The DTMgr should have been created with the correct data type organization from a language/compilerspec
      if there could be variants in datatype defintions when using the generic data type manager data organization
      for example in a generic FileDataTypeManager int and long are size 4. This will change in the future,
      but with the current implementation, beware!

@param openDTmanagers array of datatypes managers to use for undefined data types

@param filenames names of files in order to parse, could include strings with
       "#" at start, which are ignored as comments
@param includePaths paths to include files, instead of using {@code -I<includepath>} in args
@param args arguments for parsing, {@code -D<defn>=}, ( {@code -I<includepath>} use includePaths parm instead)

@param dtMgr datatypes will be populated into this provided DTMgr, can pass Program or File DTMgr

@param monitor  used to cancel or provide results

@return a formatted string of any output from pre processor parsing or C parsing

@throws ghidra.app.util.cparser.C.ParseException for catastrophic errors in C parsing
@throws ghidra.app.util.cparser.CPP.ParseException for catastrophic errors in Preprocessor macro parsing
Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CParserUtils.java