1 /*
2 this code is Public Domain
3 Peter Semiletov
4 */
5 
6 
7 
8 #include <QTextStream>
9 #include <QTextCodec>
10 #include <QDebug>
11 #include <QDir>
12 #include <QImageReader>
13 #include <QImage>
14 #include <QProcess>
15 #include <QApplication>
16 
17 #include "utils.h"
18 
19 using namespace std;
20 
21 bool boring;
22 
23 
24 /* file utils */
25 
has_css_file(const QString & path)26 bool has_css_file (const QString &path)
27 {
28   if (path.isEmpty())
29       return false;
30 
31   QDir d (path);
32   QStringList l = d.entryList();
33 
34   for (int i = 0; i < l.size(); i++)
35       {
36        if (l.at(i).endsWith(".css"))
37            return true;
38       }
39 
40   return false;
41 }
42 
43 
guess_enc_for_file(const QString & fname)44 QString guess_enc_for_file (const QString &fname)
45 {
46   if (fname.isEmpty())
47       return QString();
48 
49   QString enc = "UTF-8";
50 
51   QProcess p;
52   p.start ("enca", QStringList() << "-i" << fname);
53 
54   if (! p.waitForStarted() || ! p.waitForFinished() )
55      return "err";
56 
57   QString s = p.readAllStandardOutput();
58   if (! s.isEmpty())
59      enc = s.trimmed();
60 
61   return enc;
62 }
63 
64 
file_is_writable(const QString & fname)65 bool file_is_writable (const QString &fname)
66 {
67   if (fname.isEmpty())
68       return false;
69 
70   QFile f (fname);
71   return f.isWritable();
72 }
73 
74 
file_is_readable(const QString & fname)75 bool file_is_readable (const QString &fname)
76 {
77   if (fname.isEmpty())
78       return false;
79 
80   QFile f (fname);
81   return f.isReadable();
82 }
83 
84 
path_is_file(const QString & fname)85 bool path_is_file (const QString &fname)
86 {
87   if (fname.isEmpty())
88       return false;
89 
90   QFileInfo fi (fname);
91   return fi.isFile();
92 }
93 
94 
path_is_dir(const QString & fname)95 bool path_is_dir (const QString &fname)
96 {
97   if (fname.isEmpty())
98       return false;
99 
100   QFileInfo fi (fname);
101   return fi.isDir();
102 }
103 
104 
path_is_abs(const QString & fname)105 bool path_is_abs (const QString &fname)
106 {
107   if (fname.isEmpty())
108     return false;
109 
110   if (fname[0] == '/' || fname.indexOf (':') == 1)
111     return true;
112   else
113     return false;
114 }
115 
116 
dir_exists(const QString & path)117 bool dir_exists (const QString &path)
118 {
119   if (path.isEmpty())
120       return false;
121 
122   QDir d (path);
123   return d.exists();
124 }
125 
126 
file_exists(const QString & fileName)127 bool file_exists (const QString &fileName)
128 {
129   if (fileName.isEmpty())
130      return false;
131 
132   return QFile::exists (fileName);
133 }
134 
135 
change_file_ext(const QString & s,const QString & ext)136 QString change_file_ext (const QString &s, const QString &ext)
137 {
138   int i = s.lastIndexOf (".");
139   if (i == -1)
140      return (s + "." + ext);
141 
142   QString r (s);
143   r.truncate (++i);
144 
145   r.append (ext);
146   return r;
147 }
148 
149 
file_get_ext(const QString & file_name)150 QString file_get_ext (const QString &file_name)
151 {
152   if (file_name.isEmpty())
153       return QString();
154 
155 /*  int i = file_name.lastIndexOf (".");
156   if (i == -1)
157       return QString();
158 
159   return file_name.mid (i + 1).toLower();*/
160 
161   QFileInfo fi (file_name);
162   return fi.completeSuffix();
163 }
164 
165 
read_dir_entries(const QString & path)166 QStringList read_dir_entries (const QString &path)
167 {
168   QDir dir (path);
169   return dir.entryList (QDir::AllEntries | QDir::NoDotAndDotDot);
170 }
171 
172 
173 /* io utils */
174 
qstring_save(const QString & fileName,const QString & data,const char * enc)175 bool qstring_save (const QString &fileName, const QString &data, const char *enc)
176 {
177   if (fileName.isEmpty())
178       return false;
179 
180   QFile file (fileName);
181   if (! file.open (QFile::WriteOnly))
182       return false;
183 
184   QTextCodec *codec = QTextCodec::codecForName (enc);
185   QByteArray ba = codec->fromUnicode(data);
186 
187   file.write(ba);
188   file.close();
189 
190   return true;
191 }
192 
193 
qstring_load(const QString & fileName,const char * enc)194 QString qstring_load (const QString &fileName, const char *enc)
195 {
196   if (fileName.isEmpty())
197       return QString();
198 
199   QFile file (fileName);
200 
201   if (! file.open (QFile::ReadOnly))
202       return QString();
203 
204   QByteArray ba = file.readAll();
205   QTextCodec *codec = QTextCodec::codecForName(enc);
206 
207   file.close();
208 
209   return codec->toUnicode(ba);
210 }
211 
212 
qstring_load_first_line(const QString & fileName)213 QString qstring_load_first_line (const QString &fileName)
214 {
215   if (fileName.isEmpty())
216       return QString();
217 
218   QFile file (fileName);
219 
220   if (! file.open (QFile::ReadOnly | QFile::Text))
221      return QString();
222 
223   QTextStream in(&file);
224 
225   return in.readLine();
226 }
227 
228 
file_load(const QString & fileName)229 QByteArray file_load (const QString &fileName)
230 {
231   if (fileName.isEmpty())
232       return QByteArray();
233 
234   QFile file (fileName);
235   QByteArray b;
236 
237   if (! file.open (QFile::ReadOnly))
238       return b;
239 
240   b = file.readAll();
241 
242   return b;
243 }
244 
245 
246 /* string/stringlist utils */
247 
strlist_swap(QStringList & l,int a,int b)248 void strlist_swap (QStringList &l, int a, int b)
249 {
250   QString t = l[a];
251   l[a] = l[b];
252   l[b] = t;
253 }
254 
255 
string_between(const QString & source,const QString & sep1,const QString & sep2)256 QString string_between (const QString &source,
257                         const QString &sep1,
258                         const QString &sep2)
259 {
260   QString result;
261   int pos1 = source.indexOf (sep1);
262   if (pos1 == -1)
263      return result;
264 
265   int pos2 = source.indexOf (sep2, pos1 + sep1.size());
266   if (pos2 == -1)
267      return result;
268 
269   pos1 += sep1.size();
270 
271   result = source.mid (pos1, pos2 - pos1);
272   return result;
273 }
274 
275 
char_is_bad(const QChar & c)276 bool char_is_bad (const QChar &c)
277 {
278   if (! c.isNull() && ! c.isLetter())
279      return true;
280 
281   return false;
282 }
283 
284 
qstring_list_print(const QStringList & l)285 void qstring_list_print (const QStringList &l)
286 {
287   for (int i = 0; i < l.size(); i++)
288       qDebug() << l[i];
289 }
290 
291 
bytearray_to_stringlist(const QList<QByteArray> & a)292 QStringList bytearray_to_stringlist (const QList<QByteArray> &a)
293 {
294   QStringList r;
295 
296   for (int i = 0; i < a.size(); i++)
297        r.append (a.at(i).data());
298 
299   return r;
300 }
301 
302 
303 /* hash utils */
304 
hash_get_val(QHash<QString,QString> & h,const QString & key,const QString & def_val)305 QString hash_get_val (QHash<QString, QString> &h,
306                       const QString &key,
307                       const QString &def_val)
308 {
309   QString result = h.value (key);
310   if (result.isEmpty())
311      {
312       result = def_val;
313       h.insert (key, def_val);
314      }
315 
316   return result;
317 }
318 
319 
qstring_load_value(const QString & fileName,const QString & key,const QString & def)320 QString qstring_load_value (const QString &fileName, const QString &key, const QString &def)
321 {
322   QHash <QString, QString> h = hash_load_keyval (fileName);
323   return hash_get_val (h, key, def);
324 }
325 
326 
hash_load_keyval(const QString & fname)327 QHash <QString, QString> hash_load_keyval (const QString &fname)
328 {
329   QHash <QString, QString> result;
330 
331   if (! file_exists (fname))
332      return result;
333 
334   QStringList l = qstring_load (fname).split ("\n");
335 
336   for (QList <QString>::iterator i = l.begin(); i != l.end(); ++i)
337       {
338        QStringList sl = i->split ("=");
339        if (sl.size() > 1)
340           result.insert (sl.at(0), sl.at(1));
341       }
342 
343   return result;
344 }
345 
346 
hash_save_keyval(const QString & fname,const QHash<QString,QString> & h)347 void hash_save_keyval (const QString &fname, const QHash <QString, QString> &h)
348 {
349   QFile::remove (fname);
350 
351   QStringList l;
352 
353   QHash<QString, QString>::const_iterator i = h.constBegin();
354   while (i != h.constEnd())
355         {
356          l+= (i.key() + "=" + i.value());
357           ++i;
358         }
359 
360   qstring_save (fname, l.join ("\n"));
361 }
362 
363 
364 /* image utils */
365 
is_image(const QString & filename)366 bool is_image (const QString &filename)
367 {
368   if (filename.isEmpty())
369      return false;
370 
371   QList <QByteArray> a = QImageReader::supportedImageFormats();
372 
373   for (QList <QByteArray>::iterator i = a.begin(); i != a.end(); ++i)
374       {
375        QString t (i->data());
376        if (filename.endsWith (t.prepend ("."), Qt::CaseInsensitive))
377           return true;
378       }
379 
380   return false;
381 }
382 
383 
get_insert_image(const QString & file_name,const QString & full_path,const QString & markup_mode)384 QString get_insert_image (const QString &file_name, const QString &full_path, const QString &markup_mode)
385 {
386   if (! is_image (full_path))
387      return QString();
388 
389   QFileInfo inf (file_name);
390   QDir dir (inf.absolutePath());
391 
392   QImage img;
393   img.load (full_path);
394   QString result;
395 
396   if (markup_mode == "HTML")
397      result = QString ("<img src=\"%1\" alt=\"\" width=\"%2\" height=\"%3\">").arg (
398                         dir.relativeFilePath (full_path)).arg (img.width()).arg (img.height());
399   else
400   if (markup_mode == "XHTML")
401      result = QString ("<img src=\"%1\" border=\"0\" alt=\"\" width=\"%2\" height=\"%3\" />").arg (
402                        dir.relativeFilePath (full_path)).arg (img.width()).arg (img.height());
403   else
404   if (markup_mode == "Docbook")
405      result = QString ("<mediaobject><imageobject>\n<imagedata fileref=\"%1\"/>\n</imageobject></mediaobject>").arg (
406                         dir.relativeFilePath (full_path)) ;
407   else
408   if (markup_mode == "LaTeX")
409       result = QString ("\\includegraphics{%1}").arg (dir.relativeFilePath (full_path));
410   else
411   if (markup_mode == "Lout")
412       result = QString ("@IncludeGraphic {%1}").arg (dir.relativeFilePath (full_path));
413   else
414   if (markup_mode == "Markdown")
415       result = QString ("![alt_text]({%1})").arg (dir.relativeFilePath (full_path));
416 
417   return result;
418 }
419 
420 
421 /* class functions */
422 
iterate(QFileInfo & fi)423 void CFilesList::iterate (QFileInfo &fi)
424 {
425   qApp->processEvents();
426 
427   if (boring)
428      return;
429 
430   if (fi.isDir())
431      {
432       QDir d (fi.absoluteFilePath());
433       QFileInfoList lfi= d.entryInfoList (QDir::Dirs | QDir::Files | QDir::Readable | QDir::NoDotAndDotDot);
434       for (int i = 0; i < lfi.count(); i++)
435            iterate (lfi[i]);
436      }
437   else
438   if (fi.isFile())
439      list.append (fi.absoluteFilePath());
440 }
441 
442 
get(const QString & path)443 void CFilesList::get (const QString &path)
444 {
445   if (path.isEmpty())
446      return;
447 
448   list.clear();
449   QDir d (path);
450   QFileInfoList lfi= d.entryInfoList (QDir::Dirs | QDir::Files | QDir::Readable | QDir::NoDotAndDotDot);
451   for (int i = 0; i < lfi.count(); i++)
452       iterate (lfi[i]);
453 }
454 
455 
456 
457 #if QT_VERSION < 0x050000
458 #define ADDTEXTTYPE(expr) patterns.push_back (QRegExp (expr, Qt::CaseInsensitive));
459 #else
460 #define ADDTEXTTYPE(expr) patterns.push_back (QRegularExpression (expr, QRegularExpression::CaseInsensitiveOption));
461 #endif
462 
463 
464 
CFTypeChecker()465 CFTypeChecker::CFTypeChecker()
466 {
467 
468   ADDTEXTTYPE (".*(readme|news|changelog|todo)$");
469   ADDTEXTTYPE ("^\\..*(rc)$");
470   ADDTEXTTYPE ("^.*\\.(txt|conf|md|ini|bat|cfg|sbv|log|odt|docx|kwd|fb2|fb2.zip|fbz|abw|rtf|epub|sxw)$");
471   ADDTEXTTYPE ("^.*\\.(cpp|c|h|hh|cxx|hpp|cc|m|mm)$");
472   ADDTEXTTYPE ("^.*\\.(htm|html|xml|xhtml|ts|osm|xsl)$");
473 #if defined(POPPLER_ENABLE)
474   ADDTEXTTYPE ("^.*\\.(pdf)$");
475 #endif
476 #if defined(DJVU_ENABLE)
477   ADDTEXTTYPE ("^.*\\.(djvu)$");
478 #endif
479 
480 
481   ADDTEXTTYPE ("^.*\\.(awk)$");
482   ADDTEXTTYPE ("^.*\\.(sh)$");
483   ADDTEXTTYPE ("^.*\\.(bas|bi|vbs|vbe)$");
484   ADDTEXTTYPE ("^.*\\.(d)$");
485   ADDTEXTTYPE ("^.*\\.(f|for|f90|f95)$");
486   ADDTEXTTYPE ("^.*\\.(java|js)$");
487   ADDTEXTTYPE ("^.*\\.(ly)$");
488   ADDTEXTTYPE ("^.*\\.(lout)$");
489   ADDTEXTTYPE ("^.*\\.(lua)$");
490   ADDTEXTTYPE ("^.*\\.(asm)$");
491   ADDTEXTTYPE ("^.*\\.(nsi)$");
492   ADDTEXTTYPE ("^.*\\.(pp|pas|dpr)$");
493   ADDTEXTTYPE ("^.*\\.(pl|pm)$");
494   ADDTEXTTYPE ("^.*\\.(php)$");
495   ADDTEXTTYPE ("^.*\\.(po)$");
496   ADDTEXTTYPE ("^.*\\.(py)$");
497   ADDTEXTTYPE ("^.*\\.(r)$");
498   ADDTEXTTYPE ("^.*\\.(sd7)$");
499   ADDTEXTTYPE ("^.*\\.(tex|lyx)$");
500   ADDTEXTTYPE ("^.*\\.(vala)$");
501   ADDTEXTTYPE ("^.*\\.(v)$");
502   ADDTEXTTYPE ("^.*\\.(wiki)$");
503 }
504 
505 
506 
507 /*
508 CFTypeChecker::CFTypeChecker()
509 {
510 
511 #if QT_VERSION < 0x050000
512 
513   patterns.push_back (QRegExp (".*(readme|news|changelog|todo)$", Qt::CaseInsensitive));
514   patterns.push_back (QRegExp ("^\\..*(rc)$", Qt::CaseInsensitive));
515   patterns.push_back (QRegExp ("^.*\\.(txt|conf|md|ini|bat|cfg|sbv|log|odt|docx|kwd|fb2|abw|rtf|epub|sxw|fb2.zip)$", Qt::CaseInsensitive));
516   patterns.push_back (QRegExp ("^.*\\.(cpp|c|h|hh|cxx|hpp|cc|m|mm)$", Qt::CaseInsensitive));
517   patterns.push_back (QRegExp ("^.*\\.(htm|html|xml|xhtml|ts|osm|xsl)$", Qt::CaseInsensitive));
518 
519 #if defined(POPPLER_ENABLE)
520   patterns.push_back (QRegExp ("^.*\\.(pdf)$", Qt::CaseInsensitive));
521 #endif
522 
523 #if defined(DJVU_ENABLE)
524   patterns.push_back (QRegExp ("^.*\\.(djvu)$", Qt::CaseInsensitive));
525 #endif
526 
527   patterns.push_back (QRegExp ("^.*\\.(awk)$", Qt::CaseInsensitive));
528   patterns.push_back (QRegExp ("^.*\\.(sh)$", Qt::CaseInsensitive));
529   patterns.push_back (QRegExp ("^.*\\.(bas|bi|vbs|vbe)$", Qt::CaseInsensitive));
530   patterns.push_back (QRegExp ("^.*\\.(d)$", Qt::CaseInsensitive));
531   patterns.push_back (QRegExp ("^.*\\.(f|for|f90|f95)$", Qt::CaseInsensitive));
532   patterns.push_back (QRegExp ("^.*\\.(java|js)$", Qt::CaseInsensitive));
533   patterns.push_back (QRegExp ("^.*\\.(ly)$", Qt::CaseInsensitive));
534   patterns.push_back (QRegExp ("^.*\\.(lout)$", Qt::CaseInsensitive));
535   patterns.push_back (QRegExp ("^.*\\.(lua)$", Qt::CaseInsensitive));
536   patterns.push_back (QRegExp ("^.*\\.(asm)$", Qt::CaseInsensitive));
537   patterns.push_back (QRegExp ("^.*\\.(nsi)$", Qt::CaseInsensitive));
538   patterns.push_back (QRegExp ("^.*\\.(pp|pas|dpr)$", Qt::CaseInsensitive));
539   patterns.push_back (QRegExp ("^.*\\.(pl|pm)$", Qt::CaseInsensitive));
540   patterns.push_back (QRegExp ("^.*\\.(php)$", Qt::CaseInsensitive));
541   patterns.push_back (QRegExp ("^.*\\.(po)$", Qt::CaseInsensitive));
542   patterns.push_back (QRegExp ("^.*\\.(py)$", Qt::CaseInsensitive));
543   patterns.push_back (QRegExp ("^.*\\.(r)$", Qt::CaseInsensitive));
544   patterns.push_back (QRegExp ("^.*\\.(sd7)$", Qt::CaseInsensitive));
545   patterns.push_back (QRegExp ("^.*\\.(tex|lyx)$", Qt::CaseInsensitive));
546   patterns.push_back (QRegExp ("^.*\\.(vala)$", Qt::CaseInsensitive));
547   patterns.push_back (QRegExp ("^.*\\.(v)$", Qt::CaseInsensitive));
548   patterns.push_back (QRegExp ("^.*\\.(wiki)$", Qt::CaseInsensitive));
549 
550 #else
551 
552   patterns.push_back (QRegularExpression (".*(readme|news|changelog|todo)$", QRegularExpression::CaseInsensitiveOption));
553   patterns.push_back (QRegularExpression ("^\\..*(rc)$", QRegularExpression::CaseInsensitiveOption));
554   patterns.push_back (QRegularExpression ("^.*\\.(txt|conf|md|ini|bat|cfg|sbv|log|odt|docx|kwd|fb2|abw|rtf|epub|sxw)$", QRegularExpression::CaseInsensitiveOption));
555   patterns.push_back (QRegularExpression ("^.*\\.(cpp|c|h|hh|cxx|hpp|cc|m|mm)$", QRegularExpression::CaseInsensitiveOption));
556   patterns.push_back (QRegularExpression ("^.*\\.(htm|html|xml|xhtml|ts|osm|xsl)$", QRegularExpression::CaseInsensitiveOption));
557 
558 #if defined(POPPLER_ENABLE)
559   patterns.push_back (QRegularExpression ("^.*\\.(pdf)$", QRegularExpression::CaseInsensitiveOption));
560 #endif
561 
562 #if defined(DJVU_ENABLE)
563   patterns.push_back (QRegularExpression ("^.*\\.(djvu)$", QRegularExpression::CaseInsensitiveOption));
564 #endif
565 
566   patterns.push_back (QRegularExpression ("^.*\\.(awk)$", QRegularExpression::CaseInsensitiveOption));
567   patterns.push_back (QRegularExpression ("^.*\\.(sh)$", QRegularExpression::CaseInsensitiveOption));
568   patterns.push_back (QRegularExpression ("^.*\\.(bas|bi|vbs|vbe)$", QRegularExpression::CaseInsensitiveOption));
569   patterns.push_back (QRegularExpression ("^.*\\.(d)$", QRegularExpression::CaseInsensitiveOption));
570   patterns.push_back (QRegularExpression ("^.*\\.(f|for|f90|f95)$", QRegularExpression::CaseInsensitiveOption));
571   patterns.push_back (QRegularExpression ("^.*\\.(java|js)$", QRegularExpression::CaseInsensitiveOption));
572   patterns.push_back (QRegularExpression ("^.*\\.(ly)$", QRegularExpression::CaseInsensitiveOption));
573   patterns.push_back (QRegularExpression ("^.*\\.(lout)$", QRegularExpression::CaseInsensitiveOption));
574   patterns.push_back (QRegularExpression ("^.*\\.(lua)$", QRegularExpression::CaseInsensitiveOption));
575   patterns.push_back (QRegularExpression ("^.*\\.(asm)$", QRegularExpression::CaseInsensitiveOption));
576   patterns.push_back (QRegularExpression ("^.*\\.(nsi)$", QRegularExpression::CaseInsensitiveOption));
577   patterns.push_back (QRegularExpression ("^.*\\.(pp|pas|dpr)$", QRegularExpression::CaseInsensitiveOption));
578   patterns.push_back (QRegularExpression ("^.*\\.(pl|pm)$", QRegularExpression::CaseInsensitiveOption));
579   patterns.push_back (QRegularExpression ("^.*\\.(php)$", QRegularExpression::CaseInsensitiveOption));
580   patterns.push_back (QRegularExpression ("^.*\\.(po)$", QRegularExpression::CaseInsensitiveOption));
581   patterns.push_back (QRegularExpression ("^.*\\.(py)$", QRegularExpression::CaseInsensitiveOption));
582   patterns.push_back (QRegularExpression ("^.*\\.(r)$", QRegularExpression::CaseInsensitiveOption));
583   patterns.push_back (QRegularExpression ("^.*\\.(sd7)$", QRegularExpression::CaseInsensitiveOption));
584   patterns.push_back (QRegularExpression ("^.*\\.(tex|lyx)$", QRegularExpression::CaseInsensitiveOption));
585   patterns.push_back (QRegularExpression ("^.*\\.(vala)$", QRegularExpression::CaseInsensitiveOption));
586   patterns.push_back (QRegularExpression ("^.*\\.(v)$", QRegularExpression::CaseInsensitiveOption));
587   patterns.push_back (QRegularExpression ("^.*\\.(wiki)$", QRegularExpression::CaseInsensitiveOption));
588 
589 #endif
590 }
591 */
592 
check(const QString & fname) const593 bool CFTypeChecker::check (const QString &fname) const
594 {
595 
596 #if QT_VERSION < 0x050000
597 
598   for (size_t i = 0; i < patterns.size(); ++i)
599      {
600       if (patterns[i].exactMatch(fname))
601          return true;
602      }
603 
604 #else
605 
606   for (size_t i = 0; i < patterns.size(); ++i)
607      {
608       QRegularExpressionMatch match = patterns[i].match(fname);
609       if (match.hasMatch())
610          return true;
611       }
612 
613 #endif
614 
615   return false;
616 }
617 
618 
619 /*
620 GUI functions
621 */
622 
623