GNU Octave  4.0.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-2015 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 <jacob.dawid@cybercatalyst.com>
26 
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 
31 #include "parser.h"
32 #include "procstream.h"
33 #include <QFileInfo>
34 #include <QDir>
35 #include <QFile>
36 #include <QUrl>
37 #include <QRegExp>
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 bool
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  bool info_file_exists = info.exists ();
60  QHash<QString, QString>::iterator it;
61  for (it = _compressors_map.begin (); it != _compressors_map.end (); it++)
62  {
63  if (info_file_exists)
64  break;
65  info_file_exists = QFileInfo (info.absoluteFilePath () + "." + it.key ()).exists ();
66  }
67 
68  if (info_file_exists)
69  {
70  QString path = info.absolutePath ();
71  QString fileName = info.fileName ();
72 
73  QDir infoDir (path);
74  QStringList filter;
75  filter.append (fileName + "*");
76 
77  _info_files = infoDir.entryInfoList (filter, QDir::Files);
78 
79  parse_info_map ();
80 
81  return true;
82  }
83  else
84  return false;
85 }
86 
87 QString
89 {
90  return _info_path;
91 }
92 
93 QIODevice *
94 parser::open_file (QFileInfo & file_info)
95 {
96  QIODevice *iodevice = 0;
97  if (_compressors_map.contains (file_info.suffix ()))
98  {
99  QString command = _compressors_map.value (file_info.suffix ()).arg (file_info.absoluteFilePath ());
100  iprocstream ips (command.toStdString ());
101 
102  if (ips.bad ())
103  return 0;
104 
105  QByteArray result;
106  char buffer[1024];
107 
108  while (! ips.eof ())
109  {
110  ips.read (buffer, sizeof (buffer));
111  result.append (buffer, ips.gcount ());
112  }
113 
114  QBuffer *io = new QBuffer (this);
115  io->setData (result);
116 
117  if (!io->open (QIODevice::ReadOnly | QIODevice::Text))
118  return 0;
119 
120  iodevice = io;
121  }
122  else
123  {
124  QFile *io = new QFile (file_info.absoluteFilePath ());
125  if (!io->open (QIODevice::ReadOnly | QIODevice::Text))
126  return 0;
127  iodevice = io;
128  }
129 
130  return iodevice;
131 }
132 
133 int
134 parser::is_ref (const QString& node)
135 {
136  if (_ref_map.contains (node))
137  {
138  node_position ref = _ref_map [node];
139 
140  return ref.pos-_node_map [ref._node_name].pos;
141  }
142  if (_node_map.contains (node))
143  {
144  return 0; // node: show from the beginning
145  }
146  return -1;
147 }
148 
149 QString
150 parser::search_node (const QString& node_arg)
151 {
152  QString node = node_arg;
153 
154  QFileInfo file_info;
155  QString ref;
156 
157  if (_ref_map.contains (node))
158  {
159  ref = node;
160  node = _ref_map [ref]._node_name;
161  }
162 
163  if (_node_map.contains (node))
164  {
165  int pos = _node_map [node].pos;
166  int realPos;
167 
168  real_position (pos, file_info, realPos);
169 
170  QIODevice *io = open_file (file_info);
171  if (! io)
172  {
173  return QString ();
174  }
175 
176  seek (io, realPos);
177 
178  QString text = get_next_node (io);
179  if (!text.isEmpty())
180  {
181  return text;
182  }
183 
184  io->close ();
185  delete io;
186  }
187 
188  return QString ();
189 }
190 
191 QString
192 parser::search_node (const QString& node, QIODevice *io)
193 {
194  while (!io->atEnd ())
195  {
196  QString text = get_next_node (io);
197  if (node == get_node_name (text))
198  {
199  return text;
200  }
201  }
202 
203  return QString ();
204 }
205 
206 QString
207 parser::get_next_node (QIODevice *io)
208 {
209  QString text;
210  QByteArray line, line_buffer;
211  char c;
212  int i;
213 
214  while (!io->atEnd ())
215  {
216  io->getChar (&c);
217  if (c)
218  {
219  // first char is not equal 0
220  io->ungetChar (c);
221  line = io->readLine ();
222  }
223  else
224  {
225  // 0 was read -> image -> text length changes
226  line_buffer = io->readLine (); // image tag that is not needed
227  line = io->readLine (); // firsts line of text message
228  for (i=1; i<line_buffer.size ()+6; i++) // correct the size
229  line.insert (line.size ()-1,QByteArray(" ")); // by adding blanks
230  }
231 
232  if (line.at (0) == '"' && line.size () == 5) // end of image construct
233  line = " ";
234 
235  if (line.at(0) == 31)
236  {
237  break;
238  }
239  else
240  {
241  text.append (line);
242  }
243  }
244  return text;
245 }
246 
247 static QString
248 get_first_line (const QString& text)
249 {
250  int n = text.indexOf ("\n");
251 
252  if (n < 0)
253  {
254  return QString ();
255  }
256 
257  QString first_line = text.left (n);
258  return first_line;
259 }
260 
261 static QString
262 parser_node (const QString& text, const QString& node_name)
263 {
264  QString firstLine = get_first_line (text);
265  QStringList nodes = firstLine.split (",");
266  for (int i = 0; i < nodes.size (); i++)
267  {
268  QString node = nodes.at (i).trimmed ();
269 
270  if (node.startsWith (node_name))
271  {
272  return node.remove (0, node_name.size ()).trimmed ();
273  }
274  }
275  return QString ();
276 }
277 
278 QString
279 parser::get_node_name (const QString& text)
280 {
281  return parser_node (text, "Node:");
282 }
283 
284 QString
285 parser::get_node_up (const QString& text)
286 {
287  return parser_node (text, "Up:");
288 }
289 
290 QString
291 parser::get_node_next (const QString& text)
292 {
293  return parser_node (text, "Next:");
294 }
295 
296 QString
297 parser::get_node_prev (const QString& text)
298 {
299  return parser_node (text, "Prev:");
300 }
301 
302 static void
303 replace_links (QString& text)
304 {
305  QRegExp re ("(\\*[N|n]ote|\n\\*)([ |\n]+)([^:]+):([^:\\.,]*)([:,\\.]+)");
306  int i = 0, f;
307 
308  while ((i = re.indexIn (text,i)) != -1)
309  {
310  QString type = re.cap (1);
311  QString note = re.cap (3);
312  QString url_link = re.cap (4);
313  QString term = re.cap (5);
314 
315  if (url_link.isEmpty ())
316  {
317  url_link = note;
318  }
319 
320  term.replace(":","");
321  note.replace(":","");
322  note.replace (QRegExp ("`([^']+)'"),"\\1"); // no extra format in links
323 
324  QRegExp re_break ("(\n[ ]*)");
325 
326  if (note == "fig" || note == "tab")
327  url_link.prepend("#");
328 
329  QString href;
330  if (type == "\n*")
331  href="\n";
332 
333  if (re_break.indexIn (url_link) != -1)
334  term += re_break.cap (1);
335  else if (re_break.indexIn (re.cap (2)) != -1)
336  href = re_break.cap (1) + " ";
337  else if (re_break.indexIn (note) != -1)
338  term += re_break.cap (1);
339  note.replace(re_break,"&nbsp;");
340 
341  url_link = url_link.trimmed ();
342  url_link.replace ("\n"," ");
343  url_link.replace (QRegExp (" +")," ");
344  url_link.replace ("<b>","");
345  url_link.replace ("</b>","");
346  url_link = QUrl::toPercentEncoding (url_link, "", "'");
347 
348  href += "<font style=\"color:DarkGray; font-weight:bold;\">&raquo;</font>";
349  href += "&nbsp;<a href='" + url_link + "'>" + note + "</a>" + term;
350  f = re.matchedLength ();
351  text.replace (i,f,href);
352  i += href.size ();
353  }
354 }
355 
356 static void
358 {
359  QRegExp re ("`([^']+)'");
360  int i = 0, f;
361  while ((i = re.indexIn (text, i)) != -1)
362  {
363  QString t = re.cap (1);
364  QString bold = "<font style=\"color:SteelBlue;font-weight:bold\">" + t +
365  "</font>";
366 
367  f = re.matchedLength ();
368  text.replace (i,f,bold);
369  i += bold.size ();
370  }
371 }
372 
373 static void
374 info_to_html (QString& text)
375 {
376  text.replace ("&", "&amp;");
377  text.replace ("<", "&lt;");
378  text.replace (">", "&gt;");
379 
380  text.replace ("\n* Menu:",
381  "\n<font style=\"color:DarkRed;font-weight:bold\">Menu:</font>");
382  text.replace ("See also:",
383  "<font style=\"color:DarkRed;font-style:italic;font-weight:bold\">See also:</font>");
384  replace_links (text);
385  replace_colons (text);
386 }
387 
388 QString
389 parser::node_text_to_html (const QString& text_arg, int anchorPos,
390  const QString& anchor)
391 {
392  QString text = text_arg;
393 
394  QString nodeName = get_node_name (text);
395  QString nodeUp = get_node_up (text);
396  QString nodeNext = get_node_next (text);
397  QString nodePrev = get_node_prev (text);
398 
399  if (anchorPos > -1)
400  {
401  QString text1 = text.left (anchorPos);
402  QString text2 = text.mid (anchorPos);
403 
404  int n = text1.indexOf ("\n");
405  text1.remove (0, n);
406 
407  info_to_html (text1);
408  info_to_html (text2);
409 
410  text = text1 + "<a name='" + anchor
411  + "'/><font style=\"color:DarkBlue; font: bold monospace large;\">&diams;</font><br>&nbsp;"
412  + text2;
413  }
414  else
415  {
416  int n = text.indexOf ("\n");
417  text.remove (0, n);
418  info_to_html (text);
419  }
420 
421  QString navigationLinks = QString (
422  "<b>Section:</b> <font style=\"color:DarkRed\">%1</font><br>"
423  "<b>Previous Section:</b> <a href='%2'>%3</a><br>"
424  "<b>Next Section:</b> <a href='%4'>%5</a><br>"
425  "<b>Up:</b> <a href='%6'>%7</a><br>\n"
426  )
427  .arg (nodeName)
428  .arg (QString (QUrl::toPercentEncoding (nodePrev, "", "'")))
429  .arg (nodePrev)
430  .arg (QString (QUrl::toPercentEncoding (nodeNext, "", "'")))
431  .arg (nodeNext)
432  .arg (QString (QUrl::toPercentEncoding (nodeUp, "", "'")))
433  .arg (nodeUp);
434 
435  text.prepend ("<hr>\n<pre style=\"font-family:monospace\">");
436  text.append ("</pre>\n<hr><hr>\n");
437  text.prepend (navigationLinks);
438  text.append (navigationLinks);
439  text.prepend ("<html><body>\n");
440  text.append ("</body></html>\n");
441 
442  return text;
443 
444 }
445 
446 void
448 {
449  QRegExp re ("(Node|Ref): ([^\\0177]+)\\0177(\\d+)\n");
450  QRegExp re_files ("([^:]+): (\\d+)\n");
451  int foundCount = 0;
452 
453  for (int i = 0; i < _info_files.size (); i++)
454  {
455  QFileInfo fileInfo = _info_files.at (i);
456 
457  QIODevice *io = open_file (fileInfo);
458  if (! io)
459  {
460  continue;
461  }
462 
463  QString nodeText;
464  while (! (nodeText=get_next_node (io)).isEmpty () && foundCount < 2)
465  {
466  QString first_line = get_first_line (nodeText);
467  if (first_line.startsWith ("Tag"))
468  {
469  foundCount++;
470  int pos = 0;
471  QString last_node;
472 
473  while ((pos = re.indexIn (nodeText, pos)) != -1)
474  {
475  QString type = re.cap (1);
476  QString node = re.cap (2);
477  int index = re.cap (3).toInt ();
478 
479  if (type == "Node")
480  {
481  node_map_item item;
482  item.pos = index;
483  _node_map [node] = item;
484  last_node = node;
485  }
486  else if (type == "Ref")
487  {
488  node_position item;
489  item._node_name = last_node;
490  item.pos = index;
491  _ref_map [node] = item;
492  }
493  pos += re.matchedLength ();
494  }
495  break;
496  }
497  else if (first_line.startsWith ("Indirect:"))
498  {
499  foundCount++;
500  int pos = 0;
501 
502  while ((pos = re_files.indexIn (nodeText, pos)) != -1)
503  {
504  QString fileCap = re_files.cap (1).trimmed ();
505  int index = re_files.cap (2).toInt ();
506 
507  info_file_item item;
508  for (int j = 0; j < _info_files.size (); j++)
509  {
510  QFileInfo info = _info_files.at (j);
511  if (info.fileName ().startsWith (fileCap))
512  {
513  item.file_info = info;
514  break;
515  }
516  }
517  item.real_size = index;
518  _info_file_real_size_list.append (item);
519  pos += re_files.matchedLength ();
520  }
521 
522  }
523  }
524  io->close ();
525  delete io;
526  }
527 }
528 
529 void
530 parser::real_position (int pos, QFileInfo & file_info, int & real_pos)
531 {
532  int header = -1;
533  int sum = 0;
534  for (int i = 0; i < _info_file_real_size_list.size (); i++)
535  {
537  if (header == -1)
538  {
539  file_info = item.file_info;
540  header = item.real_size;
541  }
542 
543  if (pos < item.real_size)
544  {
545  break;
546  }
547 
548  file_info = item.file_info;
549  sum = item.real_size;
550  }
551  real_pos = pos - sum + header + 2;
552 }
553 
554 void
555 parser::seek (QIODevice *io, int pos)
556 {
557  char ch;
558  while (!io->atEnd () && pos > 0)
559  {
560  io->getChar (&ch);
561  pos--;
562  }
563 }
564 
565 static void
566 replace (QString& text, const QRegExp& re, const QString& after)
567 {
568  int pos = 0;
569 
570  while ((pos = re.indexIn (text, pos)) != -1)
571  {
572  QString cap = text.mid (pos,re.matchedLength ());
573  QString a (after);
574  a = a.arg (cap);
575  text.remove (pos, re.matchedLength ());
576  text.insert (pos, a);
577  pos += a.size ();
578  }
579 }
580 
581 QString
582 parser::global_search (const QString& text, int max_founds)
583 {
584  QString results;
585  QStringList words = text.split (" ",QString::SkipEmptyParts);
586 
587  QString re_program ("(" + words.at (0));
588  for (int i = 1; i < words.size (); i++)
589  {
590  re_program += "|" + words.at (i);
591  }
592  re_program += ")";
593 
594  QRegExp re (re_program, Qt::CaseInsensitive);
595 
596  results.append ("<html><body>\n<h1>Search results</h1>\n<b>Results for:</b> ");
597  results.append (text);
598  results.append ("<br>\n");
599 
600  for (int i = 0; i < _info_files.size (); i++)
601  {
602  QFileInfo file_info = _info_files.at (i);
603  QIODevice *io = open_file (file_info);
604  if (! io)
605  {
606  continue;
607  }
608 
609  QString node_text;
610  while (! (node_text = get_next_node (io)).isEmpty ())
611  {
612  QString firstLine = get_first_line (node_text);
613  QString node = get_node_name (node_text);
614  if (node.isEmpty ())
615  {
616  continue;
617  }
618 
619  int n = node_text.indexOf ("\n");
620  node_text.remove (0, n);
621 
622  int pos = 0;
623  int founds = 0;
624 
625  for (; founds < words.size ()
626  && node_text.indexOf (words.at (founds)) >= 0; founds++)
627  { }
628 
629  if (founds<words.size ())
630  {
631  continue;
632  }
633  founds = 0;
634 
635  while ((pos = re.indexIn (node_text, pos)) != -1
636  && founds < max_founds)
637  {
638  int line_start, line_end;
639  line_start = node_text.lastIndexOf ("\n", pos);
640  line_end = node_text.indexOf ("\n", pos);
641  QString line = node_text.mid (line_start,
642  line_end - line_start).trimmed ();
643  pos += re.matchedLength ();
644 
645  if (founds == 0)
646  {
647  results.append(
648  "<br>\n<font style=\"color:DarkGray; font-weight:bold;\">&raquo;</font> <a href='"
649  + QString(QUrl::toPercentEncoding(node,"","'")) +
650  "'>");
651  results.append (node);
652  results.append ("</a><br>\n");
653  }
654 
655  replace (line, re, "<i>%1</i>");
656  results.append (line);
657  results.append ("<br>\n");
658 
659  founds++;
660  }
661  }
662  io->close ();
663  delete io;
664  }
665 
666  results.append ("</body></html>");
667  return results;
668 }
669 
670 QString
671 parser::find_ref (const QString &ref_name)
672 {
673  QString text = "";
674 
675  QHash<QString,node_position>::iterator it;
676  for (it=_ref_map.begin (); it!=_ref_map.end (); ++it)
677  {
678  QString k = it.key ();
679  node_position p = it.value ();
680 
681  if (k == "XREF" + ref_name)
682  {
683  // found ref, so return its name
684  text = "XREF" + ref_name;
685  break;
686  }
687  }
688 
689  if (text.isEmpty ()) // try the statement-nodes
690  {
691  QHash<QString, node_map_item>::iterator itn;
692  for (itn=_node_map.begin (); itn!=_node_map.end (); ++itn)
693  {
694  QString k = itn.key ();
695  if (k == "The " + ref_name + " Statement")
696  {
697  // found ref, so return its name
698  text = k;
699  break;
700  }
701  }
702  }
703 
704  return text;
705 }
706 
QList< info_file_item > _info_file_real_size_list
Definition: parser.h:120
QString get_node_name(const QString &text)
Definition: parser.cc:279
QString _node_name
Definition: parser.h:77
void seek(QIODevice *io, int pos)
Seeks to position pos.
Definition: parser.cc:555
QString global_search(const QString &text, int maxFounds)
Definition: parser.cc:582
QString get_next_node(QIODevice *io)
Definition: parser.cc:207
QFileInfo file_info
Definition: parser.h:88
QIODevice * open_file(QFileInfo &fileInfo)
Open info files and uncompress them.
Definition: parser.cc:94
QString get_node_next(const QString &text)
Definition: parser.cc:291
QHash< QString, node_position > _ref_map
Definition: parser.h:119
QHash< QString, QString > _compressors_map
Definition: parser.h:121
static QString get_first_line(const QString &text)
Definition: parser.cc:248
static void replace_links(QString &text)
Definition: parser.cc:303
QString get_info_path()
Definition: parser.cc:88
F77_RET_T const double const double * f
QFileInfoList _info_files
Definition: parser.h:117
static void replace(QString &text, const QRegExp &re, const QString &after)
Definition: parser.cc:566
void parse_info_map()
Parses info files and gets map of node positions.
Definition: parser.cc:447
MArray< double > filter(MArray< double > &, MArray< double > &, MArray< double > &, int dim)
void real_position(int pos, QFileInfo &file_info, int &real_pos)
Calculates real position of nodes.
Definition: parser.cc:530
QString _info_path
Definition: parser.h:116
static QString parser_node(const QString &text, const QString &node_name)
Definition: parser.cc:262
parser(QObject *parent=0)
Definition: parser.cc:40
double arg(double x)
Definition: lo-mappers.h:37
QHash< QString, node_map_item > _node_map
Definition: parser.h:118
QString node_text_to_html(const QString &text, int anchorPos=-1, const QString &anchor=QString())
Translates text of node to Html.
Definition: parser.cc:389
int is_ref(const QString &node)
Checks if this node is reference.
Definition: parser.cc:134
bool set_info_path(const QString &_info_path)
Definition: parser.cc:51
QString find_ref(const QString &name)
Definition: parser.cc:671
QString get_node_prev(const QString &text)
Definition: parser.cc:297
static void info_to_html(QString &text)
Definition: parser.cc:374
QString search_node(const QString &node)
Definition: parser.cc:150
QString get_node_up(const QString &text)
Definition: parser.cc:285
static void replace_colons(QString &text)
Definition: parser.cc:357