GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
parser.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2009 P. L. Lucas
4 Copyright (C) 2012-2013 Jacob Dawid
5 
6 This file is part of Octave.
7 
8 Octave is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12 
13 Octave is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with Octave; see the file COPYING. If not, see
20 <http://www.gnu.org/licenses/>.
21 
22 */
23 
24 // Author: P. L. Lucas
25 // Author: Jacob Dawid <[email protected]>
26 
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 
31 #include "parser.h"
32 #include <QFileInfo>
33 #include <QDir>
34 #include <QFile>
35 #include <QUrl>
36 #include <QRegExp>
37 #include <QProcess>
38 #include <QBuffer>
39 
41  : QObject(p)
42 {
43  _compressors_map.insert ("bz2", "bzip2 -dc \"%1\"");
44  _compressors_map.insert ("gz", "gzip -dc \"%1\"");
45  _compressors_map.insert ("lzma", "lzma -dc \"%1\"");
46  _compressors_map.insert ("xz", "xz -dc \"%1\"");
47  _compressors_map.insert ("Z", "gunzip -c \"%1\"");
48 }
49 
50 void
51 parser::set_info_path (const QString& infoPath)
52 {
53  this->_info_path = infoPath;
54 
55  _info_files.clear ();
56 
57  QFileInfo info (infoPath);
58 
59  QString path = info.absolutePath ();
60  QString fileName = info.fileName ();
61 
62  QDir infoDir (path);
63  QStringList filter;
64  filter.append (fileName + "*");
65 
66  _info_files = infoDir.entryInfoList (filter, QDir::Files);
67  parse_info_map ();
68 }
69 
70 QString
72 {
73  return _info_path;
74 }
75 
76 QIODevice *
77 parser::open_file (QFileInfo & file_info)
78 {
79  QIODevice *iodevice = 0;
80  if ( _compressors_map.contains(file_info.suffix ()))
81  {
82  QProcess gzip;
83  gzip.start (_compressors_map.value (file_info.suffix ()).arg (file_info.absoluteFilePath ()));
84 
85  if (!gzip.waitForFinished ())
86  return 0;
87 
88  QByteArray result = gzip.readAll ();
89 
90  QBuffer *io = new QBuffer (this);
91  io->setData (result);
92 
93  if (!io->open (QIODevice::ReadOnly | QIODevice::Text))
94  return 0;
95 
96  iodevice = io;
97  }
98  else
99  {
100  QFile *io = new QFile (file_info.absoluteFilePath ());
101  if (!io->open (QIODevice::ReadOnly | QIODevice::Text))
102  return 0;
103  iodevice = io;
104  }
105 
106  return iodevice;
107 }
108 
109 int
110 parser::is_ref (const QString& node)
111 {
112  if (_ref_map.contains (node))
113  {
114  node_position ref = _ref_map [node];
115 
116  return ref.pos-_node_map [ref._node_name].pos;
117  }
118  return -1;
119 }
120 
121 QString
122 parser::search_node (const QString& node_arg)
123 {
124  QString node = node_arg;
125 
126  QFileInfo file_info;
127  QString ref;
128 
129  if (_ref_map.contains (node))
130  {
131  ref = node;
132  node = _ref_map [ref]._node_name;
133  }
134 
135  if (_node_map.contains (node))
136  {
137  int pos = _node_map [node].pos;
138  int realPos;
139 
140  real_position (pos, file_info, realPos);
141 
142  QIODevice *io = open_file (file_info);
143  if (! io)
144  {
145  return QString ();
146  }
147 
148  seek (io, realPos);
149 
150  QString text = get_next_node (io);
151  if (!text.isEmpty())
152  {
153  return text;
154  }
155 
156  io->close ();
157  delete io;
158  }
159 
160  return QString ();
161 }
162 
163 QString
164 parser::search_node (const QString& node, QIODevice *io)
165 {
166  while (!io->atEnd ())
167  {
168  QString text = get_next_node (io);
169  if (node == get_node_name (text))
170  {
171  return text;
172  }
173  }
174 
175  return QString ();
176 }
177 
178 QString
179 parser::get_next_node (QIODevice *io)
180 {
181  QString text;
182  QByteArray line, line_buffer;
183  char c;
184  int i;
185 
186  while (!io->atEnd ())
187  {
188  io->getChar (&c);
189  if (c)
190  {
191  // first char is not equal 0
192  io->ungetChar (c);
193  line = io->readLine ();
194  }
195  else
196  {
197  // 0 was read -> image -> text length changes
198  line_buffer = io->readLine (); // image tag that is not needed
199  line = io->readLine (); // firsts line of text message
200  for (i=1; i<line_buffer.size ()+6; i++) // correct the size
201  line.insert (line.size ()-1,QByteArray(" ")); // by adding blanks
202  }
203 
204  if (line.at (0) == '"' && line.size () == 5) // end of image construct
205  line = " ";
206 
207  if (line.at(0) == 31)
208  {
209  break;
210  }
211  else
212  {
213  text.append (line);
214  }
215  }
216  return text;
217 }
218 
219 static QString
220 get_first_line (const QString& text)
221 {
222  int n = text.indexOf ("\n");
223 
224  if (n < 0)
225  {
226  return QString ();
227  }
228 
229  QString first_line = text.left (n);
230  return first_line;
231 }
232 
233 static QString
234 parser_node (const QString& text, const QString& node_name)
235 {
236  QString firstLine = get_first_line (text);
237  QStringList nodes = firstLine.split (",");
238  for (int i = 0; i < nodes.size (); i++)
239  {
240  QString node = nodes.at (i).trimmed ();
241 
242  if (node.startsWith (node_name))
243  {
244  return node.remove (0, node_name.size ()).trimmed ();
245  }
246  }
247  return QString ();
248 }
249 
250 QString
251 parser::get_node_name (const QString& text)
252 {
253  return parser_node (text, "Node:");
254 }
255 
256 QString
257 parser::get_node_up (const QString& text)
258 {
259  return parser_node (text, "Up:");
260 }
261 
262 QString
263 parser::get_node_next (const QString& text)
264 {
265  return parser_node (text, "Next:");
266 }
267 
268 QString
269 parser::get_node_prev (const QString& text)
270 {
271  return parser_node (text, "Prev:");
272 }
273 
274 static void
275 replace_links (QString& text)
276 {
277  QRegExp re ("(\\*[N|n]ote|\n\\*)([ |\n]+)([^:]+):([^:\\.,]*)([:,\\.]+)");
278  int i = 0, f;
279 
280  while ( (i = re.indexIn (text,i)) != -1)
281  {
282  QString type = re.cap (1);
283  QString note = re.cap (3);
284  QString url_link = re.cap (4);
285  QString term = re.cap (5);
286 
287  if (url_link.isEmpty ())
288  {
289  url_link = note;
290  }
291 
292  term.replace(":","");
293  note.replace(":","");
294  note.replace (QRegExp ("`([^']+)'"),"\\1"); // no extra format in links
295 
296  QRegExp re_break ("(\n[ ]*)");
297 
298  if (note == "fig" || note == "tab")
299  url_link.prepend("#");
300 
301  QString href;
302  if (type == "\n*")
303  href="\n";
304 
305  if (re_break.indexIn (url_link) != -1)
306  term += re_break.cap (1);
307  else if (re_break.indexIn (re.cap (2)) != -1)
308  href = re_break.cap (1) + " ";
309  else if (re_break.indexIn (note) != -1)
310  term += re_break.cap (1);
311  note.replace(re_break,"&nbsp;");
312 
313  url_link = url_link.trimmed ();
314  url_link.replace ("\n"," ");
315  url_link.replace (QRegExp (" +")," ");
316  url_link.replace ("<b>","");
317  url_link.replace ("</b>","");
318  url_link = QUrl::toPercentEncoding (url_link, "", "'");
319 
320  href += "<img src=':/actions/icons/bookmark.png' width=10/>";
321  href += "&nbsp;<a href='" + url_link + "'>" + note + "</a>" + term;
322  f = re.matchedLength ();
323  text.replace (i,f,href);
324  i += href.size ();
325  }
326 }
327 
328 static void
330 {
331  QRegExp re ("`([^']+)'");
332  int i = 0, f;
333  while ( (i = re.indexIn (text, i)) != -1)
334  {
335  QString t = re.cap (1);
336  QString bold = "<font style=\"color:SteelBlue;font-weight:bold\">" + t +
337  "</font>";
338 
339  f = re.matchedLength ();
340  text.replace (i,f,bold);
341  i += bold.size ();
342  }
343 }
344 
345 static void
346 info_to_html (QString& text)
347 {
348  text.replace ("&", "&amp;");
349  text.replace ("<", "&lt;");
350  text.replace (">", "&gt;");
351 
352  text.replace ("\n* Menu:",
353  "\n<font style=\"color:DarkRed;font-weight:bold\">Menu:</font>");
354  text.replace ("See also:",
355  "<font style=\"color:DarkRed;font-style:italic;font-weight:bold\">See also:</font>");
356  replace_links (text);
357  replace_colons (text);
358 }
359 
360 QString
361 parser::node_text_to_html (const QString& text_arg, int anchorPos,
362  const QString& anchor)
363 {
364  QString text = text_arg;
365 
366  QString nodeName = get_node_name (text);
367  QString nodeUp = get_node_up (text);
368  QString nodeNext = get_node_next (text);
369  QString nodePrev = get_node_prev (text);
370 
371  if (anchorPos > -1)
372  {
373  QString text1 = text.left (anchorPos);
374  QString text2 = text.mid (anchorPos);
375 
376  int n = text1.indexOf ("\n");
377  text1.remove (0, n);
378 
379  info_to_html (text1);
380  info_to_html (text2);
381 
382  text = text1 + "<a name='" + anchor
383  + "'/><img src=':/actions/icons/arrow_down.png'><br>&nbsp;"
384  + text2;
385  }
386  else
387  {
388  int n = text.indexOf ("\n");
389  text.remove (0, n);
390  info_to_html (text);
391  }
392 
393  QString navigationLinks = QString (
394  "<b>Section:</b> <font style=\"color:DarkRed\">%1</font><br>"
395  "<img src=':/actions/icons/arrow_left.png'/> <b>Previous Section:</b> <a href='%2'>%3</a><br>"
396  "<img src=':/actions/icons/arrow_right.png'/> <b>Next Section:</b> <a href='%4'>%5</a><br>"
397  "<img src=':/actions/icons/arrow_up.png'/> <b>Up:</b> <a href='%6'>%7</a><br>\n"
398  )
399  .arg (nodeName)
400  .arg (QString (QUrl::toPercentEncoding (nodePrev, "", "'")))
401  .arg (nodePrev)
402  .arg (QString (QUrl::toPercentEncoding (nodeNext, "", "'")))
403  .arg (nodeNext)
404  .arg (QString (QUrl::toPercentEncoding (nodeUp, "", "'")))
405  .arg (nodeUp);
406 
407  text.prepend ("<hr>\n<pre style=\"font-family:monospace\">");
408  text.append ("</pre>\n<hr><hr>\n");
409  text.prepend (navigationLinks);
410  text.append (navigationLinks);
411  text.prepend ("<html><body>\n");
412  text.append ("</body></html>\n");
413 
414  return text;
415 
416 }
417 
418 void
420 {
421  QRegExp re ("(Node|Ref): ([^\\0177]+)\\0177(\\d+)\n");
422  QRegExp re_files ("([^:]+): (\\d+)\n");
423  int foundCount = 0;
424 
425  for (int i = 0; i < _info_files.size (); i++)
426  {
427  QFileInfo fileInfo = _info_files.at (i);
428 
429  QIODevice *io = open_file (fileInfo);
430  if (! io)
431  {
432  continue;
433  }
434 
435  QString nodeText;
436  while (! (nodeText=get_next_node (io)).isEmpty () && foundCount < 2)
437  {
438  QString first_line = get_first_line (nodeText);
439  if (first_line.startsWith ("Tag") )
440  {
441  foundCount++;
442  int pos = 0;
443  QString last_node;
444 
445  while ((pos = re.indexIn (nodeText, pos)) != -1)
446  {
447  QString type = re.cap (1);
448  QString node = re.cap (2);
449  int index = re.cap (3).toInt ();
450 
451  if (type == "Node")
452  {
453  node_map_item item;
454  item.pos = index;
455  _node_map [node] = item;
456  last_node = node;
457  }
458  else if (type == "Ref")
459  {
460  node_position item;
461  item._node_name = last_node;
462  item.pos = index;
463  _ref_map [node] = item;
464  }
465  pos += re.matchedLength ();
466  }
467  break;
468  }
469  else if (first_line.startsWith ("Indirect:"))
470  {
471  foundCount++;
472  int pos = 0;
473 
474  while ( (pos = re_files.indexIn (nodeText, pos)) != -1)
475  {
476  QString fileCap = re_files.cap (1).trimmed ();
477  int index = re_files.cap (2).toInt ();
478 
479  info_file_item item;
480  for (int j = 0; j < _info_files.size (); j++)
481  {
482  QFileInfo info = _info_files.at (j);
483  if (info.fileName ().startsWith (fileCap))
484  {
485  item.file_info = info;
486  break;
487  }
488  }
489  item.real_size = index;
490  _info_file_real_size_list.append (item);
491  pos += re_files.matchedLength ();
492  }
493 
494  }
495  }
496  io->close ();
497  delete io;
498  }
499 }
500 
501 void
502 parser::real_position (int pos, QFileInfo & file_info, int & real_pos)
503 {
504  int header = -1, sum = 0;
505  for (int i = 0; i < _info_file_real_size_list.size (); i++)
506  {
508  if (header == -1)
509  {
510  file_info = item.file_info;
511  header = item.real_size;
512  }
513 
514  if (pos < item.real_size)
515  {
516  break;
517  }
518 
519  file_info = item.file_info;
520  sum = item.real_size;
521  }
522  real_pos = pos - sum + header + 2;
523 }
524 
525 void
526 parser::seek (QIODevice *io, int pos)
527 {
528  char ch;
529  while (!io->atEnd () && pos > 0)
530  {
531  io->getChar (&ch);
532  pos--;
533  }
534 }
535 
536 static void
537 replace (QString& text, const QRegExp& re, const QString& after)
538 {
539  int pos = 0;
540 
541  while ( (pos = re.indexIn (text, pos)) != -1)
542  {
543  QString cap = text.mid (pos,re.matchedLength ());
544  QString a (after);
545  a = a.arg (cap);
546  text.remove (pos, re.matchedLength ());
547  text.insert (pos, a);
548  pos += a.size ();
549  }
550 }
551 
552 QString
553 parser::global_search (const QString& text, int max_founds)
554 {
555  QString results;
556  QStringList words = text.split (" ",QString::SkipEmptyParts);
557 
558  QString re_program ("(" + words.at (0));
559  for (int i = 1; i < words.size (); i++)
560  {
561  re_program += "|" + words.at (i);
562  }
563  re_program += ")";
564 
565  QRegExp re (re_program, Qt::CaseInsensitive);
566 
567  results.append ("<html><body>\n<h1>Search results</h1>\n<b>Results for:</b> ");
568  results.append (text);
569  results.append ("<br>\n");
570 
571  for (int i = 0; i < _info_files.size (); i++)
572  {
573  QFileInfo file_info = _info_files.at (i);
574  QIODevice *io = open_file (file_info);
575  if (! io)
576  {
577  continue;
578  }
579 
580  QString node_text;
581  while ( !(node_text = get_next_node (io)).isEmpty ())
582  {
583  QString firstLine = get_first_line (node_text);
584  QString node = get_node_name (node_text);
585  if (node.isEmpty ())
586  {
587  continue;
588  }
589 
590  int n = node_text.indexOf ("\n");
591  node_text.remove (0, n);
592 
593  int pos = 0;
594  int founds = 0;
595 
596  for (; founds < words.size ()
597  && node_text.indexOf (words.at (founds)) >= 0; founds++)
598  { }
599 
600  if (founds<words.size ())
601  {
602  continue;
603  }
604  founds = 0;
605 
606  while ((pos = re.indexIn (node_text, pos)) != -1
607  && founds < max_founds)
608  {
609  int line_start, line_end;
610  line_start = node_text.lastIndexOf ("\n", pos);
611  line_end = node_text.indexOf ("\n", pos);
612  QString line = node_text.mid (line_start,
613  line_end - line_start).trimmed ();
614  pos += re.matchedLength ();
615 
616  if (founds == 0)
617  {
618  results.append(
619  "<br>\n<img src=':/actions/icons/bookmark.png' width=10> <a href='"
620  + QString(QUrl::toPercentEncoding(node,"","'")) +
621  "'>");
622  results.append (node);
623  results.append ("</a><br>\n");
624  }
625 
626  replace (line, re, "<i>%1</i>");
627  results.append (line);
628  results.append ("<br>\n");
629 
630  founds++;
631  }
632  }
633  io->close ();
634  delete io;
635  }
636 
637  results.append ("</body></html>");
638  return results;
639 }
640 
641 QString
642 parser::find_ref (const QString &ref_name)
643 {
644  QString text = "";
645 
646  QHash<QString,node_position>::iterator it;
647  for (it=_ref_map.begin (); it!=_ref_map.end (); ++it)
648  {
649  QString k = it.key ();
650  node_position p = it.value ();
651 
652  if (k == "XREF" + ref_name)
653  {
654  // found ref, so return its name
655  text = "XREF" + ref_name;
656  }
657  }
658  return text;
659 }
660