// If input begins with pattern, fill output with end of input (without // pattern; strips trailing spaces) and return true. Return false otherwise // or if input is null static bool ReadParam(string input, string pattern, out string output) { output = String.Empty; if (input == null) { return(false); } input = input.TrimStart(' ', '\t'); if (input.Length < pattern.Length) { return(false); } if (!input.StartsWith(pattern)) { return(false); } // Avoid parsing Windows paths as escape sequences if (pattern.Trim().Equals("#:")) { input = input.Replace('\\', '/'); } output = StringEscaping.FromGettextFormat(input.Substring(pattern.Length).TrimEnd(' ', '\t')); return(true); }
// Initializes the headers from string that is in msgid "" format (i.e. list of key:value\n entries). public void ParseHeaderString(string headers) { string hdr = StringEscaping.FromGettextFormat(headers); string[] tokens = hdr.Split('\n'); headerEntries.Clear(); foreach (string token in tokens) { if (token != String.Empty) { int pos = token.IndexOf(':'); if (pos == -1) { throw new Exception(String.Format("Malformed header: '{0}'", token)); } else { string key = token.Substring(0, pos).Trim(); string value = token.Substring(pos + 1).Trim(); headerEntries[key] = value; } } } ParseHeaderDict(); }
// returns value in dummy plus any trailing lines in sr enclosed in quotes // next line is ready for parsing by function end string ParseMessage(ref string line, ref string dummy, StreamReader sr) { StringBuilder result = new StringBuilder(dummy.Substring(0, dummy.Length - 1)); while (!String.IsNullOrEmpty(line = sr.ReadLine())) { if (line[0] == '\t') { line = line.Substring(1); } if (line[0] == '"' && line[line.Length - 1] == '"') { result.Append(StringEscaping.FromGettextFormat(line.Substring(1, line.Length - 2))); } else { break; } } return(result.ToString()); }
// Parses the entire file, calls OnEntry each time msgid/msgstr pair is found. // return false if parsing failed, true otherwise public bool Parse() { string line, dummy; string mflags = String.Empty; string mstr = String.Empty; string msgctxt = String.Empty; string msgidPlural = String.Empty; string mcomment = String.Empty; List <string> mrefs = new List <string> (); List <string> mautocomments = new List <string> (); List <string> mtranslations = new List <string> (); bool hasPlural = false; using (StreamReader sr = new StreamReader(fileName, encoding)) { line = sr.ReadLine(); while (line == "") { line = sr.ReadLine(); } if (line == null) { return(false); } while (line != null) { // ignore empty special tags (except for automatic comments which we // DO want to preserve): while (line == "#," || line == "#:") { line = sr.ReadLine(); } // flags: // Can't we have more than one flag, now only the last is kept ... if (CatalogParser.ReadParam(line, "#, ", out dummy)) { mflags = dummy; //"#, " + line = sr.ReadLine(); } // auto comments: if (CatalogParser.ReadParam(line, "#. ", out dummy) || CatalogParser.ReadParam(line, "#.", out dummy)) // second one to account for empty auto comments { mautocomments.Add(dummy); line = sr.ReadLine(); } // references: else if (CatalogParser.ReadParam(line, "#: ", out dummy)) { // A line may contain several references, separated by white-space. // Each reference is in the form "path_name:line_number" // (path_name may contain spaces) dummy = dummy.Trim(); while (dummy != String.Empty) { int i = 0; while (i < dummy.Length && dummy[i] != ':') { i++; } while (i < dummy.Length && !Char.IsWhiteSpace(dummy[i])) { i++; } //store paths as Unix-type paths, but internally use native style string refpath = dummy.Substring(0, i); if (Path.DirectorySeparatorChar == '\\') { refpath = refpath.Replace('/', Path.DirectorySeparatorChar); } mrefs.Add(refpath); dummy = dummy.Substring(i).Trim(); } line = sr.ReadLine(); } // msgctxt else if (CatalogParser.ReadParam(line, "msgctxt \"", out dummy) || CatalogParser.ReadParam(line, "msgctxt\t\"", out dummy)) { msgctxt = ParseMessage(ref line, ref dummy, sr); } // msgid: else if (CatalogParser.ReadParam(line, "msgid \"", out dummy) || CatalogParser.ReadParam(line, "msgid\t\"", out dummy)) { mstr = ParseMessage(ref line, ref dummy, sr); } // msgid_plural: else if (CatalogParser.ReadParam(line, "msgid_plural \"", out dummy) || CatalogParser.ReadParam(line, "msgid_plural\t\"", out dummy)) { msgidPlural = ParseMessage(ref line, ref dummy, sr); hasPlural = true; } // msgstr: else if (CatalogParser.ReadParam(line, "msgstr \"", out dummy) || CatalogParser.ReadParam(line, "msgstr\t\"", out dummy)) { if (hasPlural) { // TODO: use logging Console.WriteLine("Broken catalog file: singular form msgstr used together with msgid_plural"); return(false); } string str = ParseMessage(ref line, ref dummy, sr); mtranslations.Add(str); if (!OnEntry(mstr, String.Empty, false, mtranslations.ToArray(), mflags, mrefs.ToArray(), mcomment, mautocomments.ToArray(), msgctxt)) { return(false); } // Cleanup vars mcomment = mstr = msgidPlural = mflags = msgctxt = String.Empty; hasPlural = false; mrefs.Clear(); mautocomments.Clear(); mtranslations.Clear(); } else if (CatalogParser.ReadParam(line, "msgstr[", out dummy)) { // msgstr[i]: if (!hasPlural) { // TODO: use logging Console.WriteLine("Broken catalog file: plural form msgstr used without msgid_plural"); return(false); } int pos = dummy.IndexOf(']'); string idx = dummy.Substring(pos - 1, 1); string label = "msgstr[" + idx + "]"; while (CatalogParser.ReadParam(line, label + " \"", out dummy) || CatalogParser.ReadParam(line, label + "\t\"", out dummy)) { StringBuilder str = new StringBuilder(dummy.Substring(0, dummy.Length - 1)); while (!String.IsNullOrEmpty(line = sr.ReadLine())) { if (line[0] == '\t') { line = line.Substring(1); } if (line[0] == '"' && line[line.Length - 1] == '"') { str.Append(line.Substring(1, line.Length - 2)); } else { if (ReadParam(line, "msgstr[", out dummy)) { pos = dummy.IndexOf(']'); idx = dummy.Substring(pos - 1, 1); label = "msgstr[" + idx + "]"; } break; } } mtranslations.Add(StringEscaping.FromGettextFormat(str.ToString())); } if (!OnEntry(mstr, msgidPlural, true, mtranslations.ToArray(), mflags, mrefs.ToArray(), mcomment, mautocomments.ToArray(), msgctxt)) { return(false); } mcomment = mstr = msgidPlural = mflags = String.Empty; hasPlural = false; mrefs.Clear(); mautocomments.Clear(); mtranslations.Clear(); } else if (CatalogParser.ReadParam(line, "#~ ", out dummy)) { // deleted lines: List <string> deletedLines = new List <string> (); deletedLines.Add(line); while (!String.IsNullOrEmpty(line = sr.ReadLine())) { // if line does not start with "#~ " anymore, stop reading if (!ReadParam(line, "#~ ", out dummy)) { break; } deletedLines.Add(line); } if (!OnDeletedEntry(deletedLines.ToArray(), mflags, null, mcomment, mautocomments.ToArray())) { return(false); } mcomment = mstr = msgidPlural = mflags = msgctxt = String.Empty; hasPlural = false; mrefs.Clear(); mautocomments.Clear(); mtranslations.Clear(); } else if (line != null && line[0] == '#') { // comment: // added line[1] != '~' check as deleted lines where being wrongly detected as comments while (!String.IsNullOrEmpty(line) && ((line[0] == '#' && line.Length < 2) || (line[0] == '#' && line[1] != ',' && line[1] != ':' && line[1] != '.' && line[1] != '~'))) { mcomment += mcomment.Length > 0 ? '\n' + line : line; line = sr.ReadLine(); } } else { line = sr.ReadLine(); } while (line == String.Empty) { line = sr.ReadLine(); } } } return(true); }