/* Java code generated by "translate.java.pss" */ import java.io.*; import java.util.regex.*; import java.util.*; // contains stack public class bashhistory { // using int instead of char so that all unicode code points are // available instead of just utf16. (emojis cant fit into utf16) private int accumulator; // counter for anything private int peep; // next char in input stream private int charsRead; // No. of chars read so far private int linesRead; // No. of lines read so far public StringBuffer workspace; // text accumulator private Stack stack; // parse token stack private int LENGTH; // tape initial length // use ArrayLists instead with .add() .get(n) and .set(n, E) // ArrayList al=new ArrayList(); private List tape; // array of token attributes private List marks; // tape marks private int tapePointer; // pointer to current cell private Reader input; // text input stream private boolean eof; // end of stream reached? private boolean flag; // not used here private StringBuffer escape; // char used to "escape" others "\" private StringBuffer delimiter; // push/pop delimiter (default is "*") private boolean markFound; // if the mark was found in tape /** make a new machine with a character stream reader */ public bashhistory(Reader reader) { this.markFound = false; this.LENGTH = 100; this.input = reader; this.eof = false; this.flag = false; this.charsRead = 0; this.linesRead = 1; this.escape = new StringBuffer("\\"); this.delimiter = new StringBuffer("*"); this.accumulator = 0; this.workspace = new StringBuffer(""); this.stack = new Stack(); this.tapePointer = 0; this.tape = new ArrayList(); this.marks = new ArrayList(); for (int ii = 0; ii < this.LENGTH; ii++) { this.tape.add(new StringBuffer("")); this.marks.add(new StringBuffer("")); } try { this.peep = this.input.read(); } catch (java.io.IOException ex) { System.out.println("read error"); System.exit(-1); } } /** read one character from the input stream and update the machine. */ public void read() { int iChar; try { if (this.eof) { System.exit(0); } this.charsRead++; // increment lines if ((char)this.peep == '\n') { this.linesRead++; } this.workspace.append(Character.toChars(this.peep)); this.peep = this.input.read(); if (this.peep == -1) { this.eof = true; } } catch (IOException ex) { System.out.println("Error reading input stream" + ex); System.exit(-1); } } /** increment tape pointer by one */ public void increment() { this.tapePointer++; if (this.tapePointer >= this.LENGTH) { this.tape.add(new StringBuffer("")); this.marks.add(new StringBuffer("")); this.LENGTH++; } } /** remove escape character */ public void unescapeChar(char c) { if (workspace.length() > 0) { String s = this.workspace.toString().replace("\\"+c, c+""); this.workspace.setLength(0); workspace.append(s); } } /** add escape character */ public void escapeChar(char c) { if (workspace.length() > 0) { String s = this.workspace.toString().replace(c+"", "\\"+c); workspace.setLength(0); workspace.append(s); } } /** whether trailing escapes \\ are even or odd */ // untested code. check! eg try: add "x \\"; print; etc public boolean isEscaped(String ss, String sSuffix) { int count = 0; if (ss.length() < 2) return false; if (ss.length() <= sSuffix.length()) return false; if (ss.indexOf(this.escape.toString().charAt(0)) == -1) { return false; } int pos = ss.length()-sSuffix.length(); while ((pos > -1) && (ss.charAt(pos) == this.escape.toString().charAt(0))) { count++; pos--; } if (count % 2 == 0) return false; return true; } /* a helper to see how many trailing \\ escape chars */ private int countEscaped(String sSuffix) { String s = ""; int count = 0; int index = this.workspace.toString().lastIndexOf(sSuffix); // remove suffix if it exists if (index > 0) { s = this.workspace.toString().substring(0, index); } while (s.endsWith(this.escape.toString())) { count++; s = s.substring(0, s.lastIndexOf(this.escape.toString())); } return count; } /** reads the input stream until the workspace end with text */ // can test this with public void until(String sSuffix) { // read at least one character if (this.eof) return; this.read(); while (true) { if (this.eof) return; if (this.workspace.toString().endsWith(sSuffix)) { if (this.countEscaped(sSuffix) % 2 == 0) { return; } } this.read(); } } /** pop the first token from the stack into the workspace */ public Boolean pop() { if (this.stack.isEmpty()) return false; this.workspace.insert(0, this.stack.pop()); if (this.tapePointer > 0) this.tapePointer--; return true; } /** push the first token from the workspace to the stack */ public Boolean push() { String sItem; // dont increment the tape pointer on an empty push if (this.workspace.length() == 0) return false; // need to get this from this.delim not "*" int iFirstStar = this.workspace.indexOf(this.delimiter.toString()); if (iFirstStar != -1) { sItem = this.workspace.toString().substring(0, iFirstStar + 1); this.workspace.delete(0, iFirstStar + 1); } else { sItem = this.workspace.toString(); this.workspace.setLength(0); } this.stack.push(sItem); this.increment(); return true; } /** swap current tape cell with the workspace */ public void swap() { String s = new String(this.workspace); this.workspace.setLength(0); this.workspace.append(this.tape.get(this.tapePointer).toString()); this.tape.get(this.tapePointer).setLength(0); this.tape.get(this.tapePointer).append(s); } /** save the workspace to file "sav.pp" */ public void writeToFile() { try { File file = new File("sav.pp"); Writer out = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(file), "UTF8")); out.append(this.workspace.toString()); out.flush(); out.close(); } catch (Exception e) { System.out.println(e.getMessage()); } } public void goToMark(String mark) { this.markFound = false; for (var ii = 0; ii < this.marks.size(); ii++) { if (this.marks.get(ii).toString().equals(mark)) { this.tapePointer = ii; this.markFound = true; } } if (this.markFound == false) { System.out.print("badmark '" + mark + "'!"); System.exit(1); } } /** parse/check/compile the input */ public void parse(InputStreamReader input) { //this is where the actual parsing/compiling code should go //but this means that all generated code must use //"this." not "mm." } public static void main(String[] args) throws Exception { String temp = ""; bashhistory mm = new bashhistory(new InputStreamReader(System.in)); // the empty recordset trick to simplify the grammar rules mm.workspace.append("recordset*"); /* add */ mm.push(); script: while (!mm.eof) { lex: { mm.read(); /* read */ if (mm.workspace.toString().matches("^[\n]+$")) { // just to debug // lines; print; mm.workspace.setLength(0); /* clear */ } /* whilenot */ while (!Character.toString((char)mm.peep).matches("^[\n]+$")) { if (mm.eof) { break; } mm.read(); } // ignore blank lines if (mm.workspace.toString().equals("") || mm.workspace.toString().matches("^\\p{Space}+$")) { mm.workspace.setLength(0); /* clear */ break lex; } mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); if (mm.workspace.toString().startsWith("#") && !mm.workspace.toString().equals("#")) { if (mm.workspace.toString().matches("^[#0123456789]+$")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("timestamp*"); /* add */ mm.push(); break lex; } mm.workspace.setLength(0); /* clear */ mm.workspace.append("comment*"); /* add */ mm.push(); break lex; } // tag the command as trivial if it is // for later removal. If there is a comment above it we may keep it anyway // tag as trivial all commands less than 5 characters if (mm.workspace.length() > 0) { /* clip */ mm.workspace.delete(mm.workspace.length() - 1, mm.workspace.length()); } if (mm.workspace.length() > 0) { /* clip */ mm.workspace.delete(mm.workspace.length() - 1, mm.workspace.length()); } if (mm.workspace.length() > 0) { /* clip */ mm.workspace.delete(mm.workspace.length() - 1, mm.workspace.length()); } if (mm.workspace.length() > 0) { /* clip */ mm.workspace.delete(mm.workspace.length() - 1, mm.workspace.length()); } if (mm.workspace.toString().equals("")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("trivial*"); /* add */ mm.push(); break lex; } mm.workspace.setLength(0); /* clear */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.workspace.toString().startsWith("df ") || mm.workspace.toString().equals("df") || mm.workspace.toString().startsWith("du ") || mm.workspace.toString().startsWith("mv ") || mm.workspace.toString().startsWith("cp ") || mm.workspace.toString().startsWith("less ") || mm.workspace.toString().startsWith("vim ") || mm.workspace.toString().startsWith("rm ") || mm.workspace.toString().startsWith("mkdir ") || mm.workspace.toString().startsWith("find ") || mm.workspace.toString().startsWith("locate ") || mm.workspace.toString().startsWith("cd ") || mm.workspace.toString().equals("cd") || mm.workspace.toString().startsWith("ls ") || mm.workspace.toString().equals("ls") || mm.workspace.toString().equals("pwd") || mm.workspace.toString().equals("hist") || mm.workspace.toString().equals("books") || mm.workspace.toString().equals("bk") || mm.workspace.toString().equals("ho") || mm.workspace.toString().equals("updatedb") || mm.workspace.toString().equals("bashrc") || mm.workspace.toString().equals("vimrc") || mm.workspace.toString().equals("os") || mm.workspace.toString().equals("cos") || mm.workspace.toString().equals("ccos") || mm.workspace.toString().equals("make")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("trivial*"); /* add */ mm.push(); break lex; } mm.workspace.setLength(0); /* clear */ mm.workspace.append("command*"); /* add */ mm.push(); } parse: while (true) { // for debugging // add "line "; lines; add " char "; chars; add ": "; print; clear; //add "line "; lines; add ": "; print; clear; //unstack; print; stack; add "\n"; print; clear; // ---------------- // 2 tokens mm.pop(); mm.pop(); // ignore duplicated timestamps. if (mm.workspace.toString().equals("timestamp*timestamp*")) { mm.workspace.setLength(0); /* clear */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.workspace.append("timestamp*"); /* add */ mm.push(); continue parse; } // handle multiline comments if (mm.workspace.toString().equals("comment*comment*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.workspace.append("\n"); /* add */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.workspace.append("comment*"); /* add */ mm.push(); continue parse; } // dont need because an initial recordset always exists //"record*record*","recordset*record*" { if (mm.workspace.toString().equals("recordset*record*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.workspace.append("\n"); /* add */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ // debug code // a+; count; add " record!\n"; print; clear; mm.workspace.append("recordset*"); /* add */ mm.push(); continue parse; } // this will be compiled differently from r*r* if (mm.workspace.toString().equals("recordset*command*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.workspace.append("\n"); /* add */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.workspace.append("recordset*"); /* add */ mm.push(); continue parse; } if (mm.workspace.toString().equals("recordset*trivial*")) { mm.accumulator++; /* a+ */ // count filtered commands mm.workspace.setLength(0); /* clear */ mm.workspace.append("recordset*"); /* add */ mm.push(); continue parse; } if (mm.eof) { // clean up trailing comments etc if (mm.workspace.toString().equals("recordset*timestamp*") || mm.workspace.toString().equals("recordset*comment*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("recordset*record*"); /* add */ mm.push(); mm.push(); continue parse; } } // 3 tokens mm.pop(); // remove trivial commands without comments if (mm.workspace.toString().equals("recordset*timestamp*trivial*")) { mm.accumulator++; /* a+ */ // count filtered commands mm.workspace.setLength(0); /* clear */ mm.workspace.append("recordset*"); /* add */ mm.push(); continue parse; } // ignore duplicated timestamps. if (mm.workspace.toString().equals("timestamp*comment*timestamp*")) { mm.workspace.setLength(0); /* clear */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.increment(); /* ++ */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.workspace.setLength(0); /* clear */ mm.workspace.append("comment*timestamp*"); /* add */ mm.push(); mm.push(); continue parse; } // amalgamate comments before and after the timestamp if (mm.workspace.toString().equals("comment*timestamp*comment*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.increment(); /* ++ */ mm.increment(); /* ++ */ mm.workspace.append("\n"); /* add */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.workspace.append("comment*timestamp*"); /* add */ mm.push(); mm.push(); continue parse; } if (mm.workspace.toString().equals("comment*timestamp*command*") || mm.workspace.toString().equals("comment*timestamp*trivial*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.workspace.append("\n"); /* add */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.workspace.append("\n"); /* add */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.workspace.append("record*"); /* add */ mm.push(); continue parse; } // dont remove trivial commands with comments if (mm.workspace.toString().equals("timestamp*comment*command*") || mm.workspace.toString().equals("timestamp*comment*trivial*")) { mm.workspace.setLength(0); /* clear */ // switch the order to make comment precede timestamp mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.workspace.append("\n"); /* add */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.workspace.append("\n"); /* add */ mm.increment(); /* ++ */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.workspace.append("record*"); /* add */ mm.push(); continue parse; } if (mm.workspace.toString().equals("recordset*timestamp*command*")) { mm.workspace.setLength(0); /* clear */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.workspace.append("\n"); /* add */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.workspace.setLength(0); /* clear */ mm.workspace.append("recordset*record*"); /* add */ mm.push(); mm.push(); continue parse; } // resolve commands and trivial command with comments if (mm.workspace.toString().equals("recordset*comment*command*") || mm.workspace.toString().equals("recordset*comment*trivial*")) { mm.workspace.setLength(0); /* clear */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.workspace.append("\n"); /* add */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.workspace.setLength(0); /* clear */ mm.workspace.append("recordset*record*"); /* add */ mm.push(); mm.push(); continue parse; } mm.push(); mm.push(); mm.push(); if (mm.eof) { mm.pop(); mm.pop(); if (!mm.workspace.toString().equals("recordset*")) { mm.push(); mm.push(); mm.workspace.append("# History file did not parse well!\n"); /* add */ System.out.print(mm.workspace); /* print */ mm.workspace.setLength(0); /* clear */ mm.workspace.append("# Parse stack was: "); /* add */ System.out.print(mm.workspace); /* print */ mm.workspace.setLength(0); /* clear */ while (mm.pop()); /* unstack */ mm.workspace.append("\n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } if (mm.workspace.toString().equals("recordset*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.workspace.append("\n# History file parsed and filtered by pars/eg/bash.history.pss \n"); /* add */ mm.workspace.append("# "); /* add */ mm.workspace.append(mm.accumulator); /* count */ mm.workspace.append(" trivial commands (without preceding comments) were removed.\n"); /* add */ System.out.print(mm.workspace); /* print */ } } break parse; } } } }