/******************************************************************************* * Copyright (c) 2007 Red Hat Inc.. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat Incorporated - initial API and implementation *******************************************************************************/ package org.eclipse.linuxtools.cdt.libhover.texinfoparsers; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; //This file contains a texinfo parser that can be //run to create the Autotools glibc.xml file. //Usage is as follows: //1. compile this file using javac //2. run this file using java, passing the //arguments: ${glibc_source_path}/manual glibc.xml public class ParseGlibcTexinfo { static final boolean DEBUG = false; // 1 static final String ATcmd = "(@\\w*)"; // 3 4 static final String RtnTypeM = "(\\{([^\\}]*)\\})"; static final int RtnTypeMIndex = 4; // 5 static final String RtnTypeS = "(\\w*)"; static final int RtnTypeSIndex = 5; // 2 static final String RtnType = "(" + RtnTypeM + "|" + RtnTypeS + ")"; // 6 static final String FunctionName = "(\\w*)"; static final int FunctionNameIndex = 6; // 7 8 static final String Parms = "(\\((.*)\\))"; static final int ParmsIndex = 8; static final String rest = ".*"; static final String WhiteSpace = "\\s*"; static final Pattern DeftypefunPattern = Pattern.compile("^" + ATcmd + WhiteSpace + RtnType + WhiteSpace + FunctionName + WhiteSpace + Parms + rest, Pattern.MULTILINE + Pattern.CASE_INSENSITIVE); static final String TPDataType = "\\{[^\\}]*\\}"; // 3 4 static final String TPTypeM = "(\\{([^\\}]*)\\})"; static final int TPTypeMIndex = 4; // 5 static final String TPTypeS = "(\\w*)"; static final int TPTypeSIndex = 5; // 2 static final String TPType = "(" + TPTypeM + "|" + TPTypeS + ")"; static final Pattern DeftpPattern = Pattern.compile("^" + ATcmd + WhiteSpace + TPDataType + WhiteSpace + TPType + rest, Pattern.MULTILINE + Pattern.CASE_INSENSITIVE); static final Pattern IncludePattern = Pattern.compile("^@comment ((\\w*/)*\\w*\\.h\\s*)*\\s*$"); static final Pattern PindexPattern = Pattern.compile("^@pindex\\s*\\w*\\s*$"); static final Pattern ParmBracketPattern = Pattern.compile("\\((.*)\\)"); static final Pattern IndexPattern = Pattern.compile("@\\w*index\\s+[a-zA-Z0-9_@\\{\\}]*"); static final Pattern IndexPattern2 = Pattern.compile("@\\w*index\\{[a-zA-Z0-9_@\\{\\}]*\\}"); static final Pattern ExamplePattern = Pattern.compile("@example"); static final Pattern EndExamplePattern = Pattern.compile("@end\\s+example"); static final Pattern EnumeratePattern = Pattern.compile("@enumerate"); static final Pattern EndEnumeratePattern = Pattern.compile("@end\\s+enumerate"); static final Pattern VerbatimPattern = Pattern.compile("@verbatim"); static final Pattern ItemPattern = Pattern.compile("@item"); static final Pattern NoIndentPattern = Pattern.compile("@noindent"); static final Pattern BRPattern = Pattern.compile("<br>"); static final Pattern EOLPattern = Pattern.compile("<eol>"); static final Pattern EndVerbatimPattern = Pattern.compile("@end\\s+verbatim"); static final Pattern TableSampItemPattern = Pattern.compile("(@table\\s*@samp.*)@item\\s*([a-zA-Z_0-9+\\-<>/ ]*)<eol>(.*@end\\s*table)", Pattern.DOTALL); static final Pattern TableAsisItemPattern = Pattern.compile("(@table\\s*@asis.*)@item\\s*([a-zA-Z_0-9+\\-,<>/ ]*)<eol>(.*@end\\s*table)", Pattern.DOTALL); static final Pattern TableSampPattern = Pattern.compile("@table\\s*@samp", Pattern.MULTILINE); static final Pattern TableAsisPattern = Pattern.compile("@table\\s*@asis", Pattern.MULTILINE); static final Pattern EndTablePattern = Pattern.compile("@end\\s+table"); static final Pattern DotsPattern = Pattern.compile("@dots\\{\\}"); static final Pattern ItemizeMinusPattern= Pattern.compile("@itemize\\s+@minus" + "(.*)" + "@end\\s+itemize", Pattern.MULTILINE); static final Pattern ItemizeBulletPattern= Pattern.compile("@itemize\\s+@bullet" + "(.*)" + "@end\\s+itemize", Pattern.MULTILINE); static final Pattern XrefPattern = Pattern.compile("@xref\\{[^\\}]*\\}", Pattern.MULTILINE); static final Pattern CommandPattern = Pattern.compile("@command\\{([^\\}]*)\\}"); static final Pattern KbdPattern = Pattern.compile("@kbd\\{([^\\}]*)\\}"); static final Pattern RPattern = Pattern.compile("@r\\{([^\\}]*)\\}"); static final Pattern FilePattern = Pattern.compile("@file\\{([^\\}]*)\\}"); static final Pattern VarPattern = Pattern.compile("@var\\{([^\\}]*)\\}"); static final Pattern OVarPattern = Pattern.compile("@ovar\\{([^\\}]*)\\}"); static final Pattern DVarPattern = Pattern.compile("@dvar\\{([^\\},\\,]*),([^\\}]*)\\}"); static final Pattern CodePattern = Pattern.compile("@code\\{([^\\}]*)\\}"); static final Pattern EmphPattern = Pattern.compile("@emph\\{([^\\}]*)\\}"); static final Pattern SampPattern = Pattern.compile("@samp\\{([^\\}]*)\\}"); static final Pattern OptionPattern = Pattern.compile("@option\\{([^\\}]*)\\}"); static final Pattern TagPattern = Pattern.compile("@\\w*\\{([^\\}]*)\\}"); static final Pattern AmpersandPattern = Pattern.compile("&"); static final Pattern LeftAnglePattern = Pattern.compile("<"); static final Pattern RightAnglePattern = Pattern.compile(">"); static List<String> IncludeList = new ArrayList<>(); static class FunctionDef { String ReturnType; String FunctionName; String[] Parameters; Object[] IncludeList; } static class TPElement { String Content; String Synopsis; } static class TPDef { String TPType; String TPName; String TPSynopsis; TPElement[] TPElements; Object[] IncludeList; } private static FunctionDef BuildFunctionDef(Matcher m) { FunctionDef fd = new FunctionDef(); fd.ReturnType = ((null != m.group(RtnTypeSIndex)) ? m.group(RtnTypeSIndex) : m.group(RtnTypeMIndex)); fd.FunctionName = m.group(FunctionNameIndex); if (null != m.group(ParmsIndex)) { String tt = TexinfoUtils.stripProtoTags(m.group(ParmsIndex)); String[] parms = tt.split(",\\s"); fd.Parameters = parms; } if (IncludeList.size() > 0) { fd.IncludeList = IncludeList.toArray(); IncludeList.clear(); } return fd; } private static void HandleDeftp(BufferedWriter os, BufferedReader is, String s) throws IOException { TPDef td = new TPDef(); String il; String Synopsis = null; boolean ItemsAccumulating = false; TPElement tpe = new TPElement(); List<TPElement> ElementList = new ArrayList<>(); Matcher m = DeftpPattern.matcher(s); if (m.matches()) { if (null != m.group(TPTypeMIndex)) { String[] ss = m.group(TPTypeMIndex).split("\\s"); switch(ss.length) { case 0: td.TPType = ""; td.TPName = "type"; break; case 1: td.TPType = "type"; td.TPName = ss[0]; break; case 2: td.TPType = ss[0]; td.TPName = ss[1]; break; default: td.TPType = "type"; td.TPName = ss[ss.length - 1]; break; } } else { td.TPType = "dtype"; td.TPName = m.group(TPTypeSIndex); } while (null != (il = is.readLine())) { if (il.startsWith("@end deftp")) { WriteString(os, " <construct id=\"" + td.TPType + "-" + td.TPName + "\" type=\"" + td.TPType + "\">"); WriteString(os, " <structure>"); if (null != td.TPSynopsis) { WriteSynopsis(os, td.TPSynopsis, false); } if (ElementList.size() > 0) { WriteString(os, " <elements>"); for (int ee = 0; ee < ElementList.size(); ee++) { TPElement ttt = ElementList.get(ee); WriteString(os, " <element content=\"" + ttt.Content + "\">"); if (null != ttt.Synopsis) { WriteSynopsis(os, ttt.Synopsis, true); } WriteString(os, " </element>"); } WriteString(os, " </elements>"); } WriteString(os, " </structure>"); WriteString(os, " </construct>"); return; } else if (il.startsWith("@item")) { if (ItemsAccumulating) { tpe.Synopsis = Synopsis; ElementList.add(tpe); } else { td.TPSynopsis = Synopsis; ItemsAccumulating = true; } Synopsis = null; tpe = new TPElement(); tpe.Content = TexinfoUtils.transformTags(il.replaceFirst("@item ", "")); } else { if (!il.startsWith("@table")) { Synopsis = ((Synopsis == null) ? "" : Synopsis + " " ) + ((il.length() == 0) ? "<br><br>" : il + "<eol>"); } } } } } private static FunctionDef HandleFunctionDef(BufferedReader is, String s) throws IOException { FunctionDef fd; Matcher m = DeftypefunPattern.matcher(s); if (m.matches()) { fd = BuildFunctionDef(m); } else { // assume the line got split and retry String il = is.readLine(); m = DeftypefunPattern.matcher(s + il); if (m.matches()) { fd = BuildFunctionDef(m); } else { fd = null; } } return fd; } private static void WriteString(BufferedWriter os, String s) throws IOException { // System.out.println(s); os.write(s+"\n", 0, 1+s.length()); } private static void CreateHeader(BufferedWriter os) throws IOException { WriteString(os, "<!-- This file automatically generated by an Eclipse utility -->"); WriteString(os, "<!DOCTYPE descriptions ["); WriteString(os, ""); WriteString(os, " <!ELEMENT descriptions (construct)*>"); WriteString(os, ""); WriteString(os, " <!ELEMENT construct (structure|function)*>"); WriteString(os, " <!ATTLIST construct"); WriteString(os, " id ID #REQUIRED"); WriteString(os, " type CDATA #REQUIRED"); WriteString(os, " >"); WriteString(os, ""); WriteString(os, " <!ELEMENT structure (synopsis?, elements?)?>"); WriteString(os, ""); WriteString(os, " <!ELEMENT elements (element*)>"); WriteString(os, ""); WriteString(os, " <!ELEMENT element (synopsis*)>"); WriteString(os, " <!ATTLIST element"); WriteString(os, " content CDATA #REQUIRED"); WriteString(os, " >"); WriteString(os, ""); WriteString(os, " <!ELEMENT synopsis (#PCDATA)*>"); WriteString(os, ""); WriteString(os, " <!ELEMENT function (prototype,headers?,synopsis)>"); WriteString(os, " <!ATTLIST function"); WriteString(os, " returntype CDATA #REQUIRED"); WriteString(os, " >"); WriteString(os, ""); WriteString(os, " <!ELEMENT prototype (parameter+)?>"); WriteString(os, ""); WriteString(os, " <!ELEMENT parameter (#PCDATA)*>"); WriteString(os, " <!ATTLIST parameter"); WriteString(os, " content CDATA #REQUIRED"); WriteString(os, " >"); WriteString(os, ""); WriteString(os, " <!ELEMENT headers (header+)?>"); WriteString(os, ""); WriteString(os, " <!ELEMENT header (#PCDATA)*>"); WriteString(os, " <!ATTLIST header"); WriteString(os, " filename CDATA #REQUIRED"); WriteString(os, " >"); WriteString(os, ""); WriteString(os, "]>"); WriteString(os, ""); } private static void CreateTrailer(BufferedWriter os) throws IOException { WriteString(os, "</descriptions>"); } private static void WriteSynopsis(BufferedWriter os, String Synopsis, boolean indent) throws IOException { String ss = TexinfoUtils.transformTags(Synopsis); String[] tt = ss.split("<eol>"); String aa = ""; String spaces = indent ? " " : " "; WriteString(os, spaces + "<synopsis>"); if (null != Synopsis) { for (int pp = 0; pp < tt.length; pp++) { WriteString(os, spaces + tt[pp]); } } if (aa.length() > 0) { WriteString(os, " " + aa); } WriteString(os, spaces + "</synopsis>"); } private static String HandleInclude (String srcdir, String line, String Synopsis) { Pattern p = Pattern.compile("@include\\s+(.*?)\\.texi"); Matcher mm = p.matcher(line); if (mm.find()) { String il; String fileName = (srcdir.endsWith("/") ? srcdir : srcdir + "/") + mm.replaceAll("examples/$1"); try (BufferedReader is = new BufferedReader(new FileReader(fileName))) { while (null != (il = is.readLine())) { // C Help does not ignore "<" or ">" inside a <pre> or <samp> tag // so we have to prepare for two levels of indirection. The // first is for xml to interpret and the second is for the // C Help processor to interpret. So, we put < and > which // will be transformed into &lt; by the tag transformer. Pattern p1 = Pattern.compile("<"); Pattern p2 = Pattern.compile(">"); Matcher mm1 = p1.matcher(il); il = mm1.replaceAll("<"); Matcher mm2 = p2.matcher(il); il = mm2.replaceAll(">"); Synopsis = ((Synopsis == null) ? "" : Synopsis + " " ) + ((il.length() == 0) ? "<br><br>" : il + "<eol>"); } } catch (IOException e) { System.out.println("IOException reading example file"); } } return Synopsis; } private static void HandleDeftypefun(BufferedWriter os, BufferedReader is, String s, String srcdir) throws IOException { String il; FunctionDef fd; List<FunctionDef> FDefs = new ArrayList<>(); String Synopsis = null; if (null != (fd = HandleFunctionDef(is, s))) { FDefs.add(fd); } while (null != (il = is.readLine())) { Matcher mm = IncludePattern.matcher(il); if (il.startsWith("@deftypefunx")) { if (null != (fd = HandleFunctionDef(is, il))) { FDefs.add(fd); } } else if (mm.matches()) { if (!IncludeList.contains(mm.group(1))) { IncludeList.add(mm.group(1)); } } else if (il.startsWith("@comment") || il.startsWith("@c ") || il.startsWith("@pindex")) { // ignore } else if (il.startsWith("@include") && fd != null) { Synopsis = HandleInclude(srcdir, il, Synopsis); } else if (il.startsWith("@end deftypefun")) { for (int kk = 0; kk < FDefs.size(); kk++) { fd = FDefs.get(kk); WriteString(os, " <construct id=\"function-" + fd.FunctionName + "\" type=\"function\">"); WriteString(os, " <function returntype=\"" + fd.ReturnType + "\">"); WriteString(os, " <prototype>"); String[] parms = fd.Parameters; for (int i = 0; i < parms.length; i++) { String parm = TexinfoUtils.stripProtoTags(parms[i]); WriteString(os, " <parameter content=\"" + parm + "\"/>"); } WriteString(os, " </prototype>"); if (fd.IncludeList != null) { WriteString(os, " <headers>"); Object[] incs = fd.IncludeList; for (int j = 0; j < incs.length; j++) { String inc = (String)incs[j]; WriteString(os, " <header filename = \"" + inc + "\"/>"); } WriteString(os, " </headers>"); } if (null != Synopsis) { WriteSynopsis(os, Synopsis, false); } WriteString(os, " </function>"); WriteString(os, " </construct>"); } return; } else { Synopsis = ((Synopsis == null) ? "" : Synopsis + " " ) + ((il.length() == 0) ? "<br><br>" : il + "<eol>"); } } FDefs.clear(); } private static class OnlyTexi implements FilenameFilter { @Override public boolean accept(File dir, String s) { return (s.endsWith(".texi")) ? true : false; } } public static void BuildXMLFromTexinfo(String srcdir, String dstdir) { try { BufferedWriter os = new BufferedWriter(new FileWriter(dstdir)); CreateHeader(os); WriteString(os, "<descriptions>"); try { String[] dir = new java.io.File(srcdir).list(new OnlyTexi()); for (int i = 0; i < dir.length; i++) { String qFile = srcdir.endsWith("/") ? srcdir + dir[i] : srcdir + "/" + dir[i]; try (BufferedReader is = new BufferedReader(new FileReader(qFile))) { String il; while (null != (il = is.readLine())) { Matcher mm = IncludePattern.matcher(il); if (il.startsWith("@deftypefun")) { // handle @deftypefun[x] HandleDeftypefun(os, is, il, srcdir); } else if (il.startsWith("@deftp")) { // handle @deftp HandleDeftp(os, is, il); } else if (mm.matches()) { // handle @comment <include_file> if (!IncludeList.contains(mm.group(1))) { IncludeList.add(mm.group(1)); } } else if (il.startsWith("@end deftypefn")) { // Handle accumulated header file comments that are in // constructs we aren't parsing. IncludeList.clear(); } } } catch (IOException e) { System.out.println("Input File IOException: " + e); return; } } } catch (NullPointerException e) { System.out.println("NullPointerException: " + e); return; } CreateTrailer(os); os.close(); } catch (IOException e) { System.out.println("Output File IOException: " + e); return; } } public static void main(String[] args) { BuildXMLFromTexinfo(args[0], args[1]); } }