public ProgramInfo getProgramInfo() throws SAXException, IOException {
		if (info != null) {
			return info;
		}

		info = new ProgramInfo();

		XmlPullParser parser =
			XmlPullParserFactory.create(file, new MyErrorHandler(new MessageLog()), false);

		boolean isFileValid = false;

		String ver = parser.getProcessingInstruction(PROGRAM_DTD, "VERSION");
		if (ver != null) {
			try {
				dtdVersion = Integer.parseInt(ver);
			}
			catch (NumberFormatException e) {
				Msg.debug(this, "Unable to parse DTD: " + ver);
			}
		}

		while (parser.hasNext()) {
			XmlElement element = parser.next();
			String name = element.getName();
			if (name.equals("PROGRAM") && element.isStart()) {
				isFileValid = true;

				info.programName = element.getAttribute("NAME");
				info.exePath = element.getAttribute("EXE_PATH");
				info.exeFormat = element.getAttribute("EXE_FORMAT");
				info.imageBase = element.getAttribute("IMAGE_BASE");
			}
			else if (name.equals("INFO_SOURCE") && element.isStart()) {
				info.user = element.getAttribute("USER");
				info.setTool(element.getAttribute("TOOL"));
				info.timestamp = element.getAttribute("TIMESTAMP");
				info.version = element.getAttribute("VERSION");

				if (isOldXml() && info.isGhidra()) {
					info.programName = element.getAttribute("FILE");
				}
			}
			else if (name.equals("PROCESSOR") && element.isStart()) {
				String languageString = element.getAttribute("LANGUAGE_PROVIDER");
				LanguageCompilerSpecPair pair =
					OldLanguageMappingService.processXmlLanguageString(languageString);
				if (pair != null) {
					info.languageID = pair.languageID;
					info.compilerSpecID = pair.compilerSpecID;
				}
				else {
					info.setCompilerSpecID(getForCompilerTag(parser));
				}
				info.processorName = element.getAttribute("NAME");
				info.family = element.getAttribute("FAMILY");
				info.addressModel = element.getAttribute("ADDRESS_MODEL");
				info.endian = element.getAttribute("ENDIAN");

				break;
			}
		}

		parser.dispose();

		if (!isFileValid) {
			info = null;
		}

		return info;
	}
Returns the program info from the underlying file. T``his method
does not make sense to invoke if a write is being performed
to a new file.
@return the program info
@throws SAXException if an XML error occurs
@throws IOException if an I/O error occurs
Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/ProgramXmlMgr.java