1 /**
2  * @file src/megacmdcommonutils.cpp
3  * @brief MEGAcmd: Auxiliary methods
4  *
5  * (c) 2013 by Mega Limited, Auckland, New Zealand
6  *
7  * This file is part of the MEGAcmd.
8  *
9  * MEGAcmd is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  *
13  * @copyright Simplified (2-clause) BSD License.
14  *
15  * You should have received a copy of the license along with this
16  * program.
17  */
18 
19 #include "megacmdcommonutils.h"
20 
21 #ifdef _WIN32
22 #include <Shlwapi.h> //PathAppend
23 #include <Shellapi.h> //CommandLineToArgvW
24 #else
25 #include <sys/ioctl.h> // console size
26 #include <unistd.h>
27 #endif
28 
29 #include <iomanip>
30 #include <fstream>
31 #include <string.h>
32 #include <algorithm>
33 #include <sstream>
34 #include <limits.h>
35 #include <iterator>
36 
37 #ifdef _WIN32
38 namespace mega {
39 //override for the log. This is required for compiling, otherwise SimpleLog won't compile.
operator <<(std::ostringstream & ostr,const std::wstring & str)40 std::ostringstream & operator<< ( std::ostringstream & ostr, const std::wstring & str)
41 {
42     std::string s;
43     megacmd::localwtostring(&str,&s);
44     ostr << s;
45     return ( ostr );
46 }
47 }
48 #endif
49 
50 namespace megacmd {
51 using namespace std;
52 
53 #ifdef _WIN32
operator <<(std::wostream & ostr,std::string const & str)54 std::wostream & operator<< ( std::wostream & ostr, std::string const & str )
55 {
56     std::wstring toout;
57     stringtolocalw(str.c_str(),&toout);
58     ostr << toout;
59     return ( ostr );
60 }
61 
operator <<(std::wostream & ostr,const char * str)62 std::wostream & operator<< ( std::wostream & ostr, const char * str )
63 {
64     std::wstring toout;
65     stringtolocalw(str,&toout);
66     ostr << toout;
67     return ( ostr );
68 }
69 
70 // convert UTF-8 to Windows Unicode wstring
stringtolocalw(const char * path,std::wstring * local)71 void stringtolocalw(const char* path, std::wstring* local)
72 {
73     // make space for the worst case
74     local->resize((strlen(path) + 1) * sizeof(wchar_t));
75 
76     int wchars_num = MultiByteToWideChar(CP_UTF8, 0, path,-1, NULL,0);
77     local->resize(wchars_num);
78 
79     int len = MultiByteToWideChar(CP_UTF8, 0, path,-1, (wchar_t*)local->data(), wchars_num);
80 
81     if (len)
82     {
83         local->resize(len-1);
84     }
85     else
86     {
87         local->clear();
88     }
89 }
90 
91 //widechar to utf8 string
localwtostring(const std::wstring * wide,std::string * multibyte)92 void localwtostring(const std::wstring* wide, std::string *multibyte)
93 {
94     if( !wide->empty() )
95     {
96         int size_needed = WideCharToMultiByte(CP_UTF8, 0, wide->data(), (int)wide->size(), NULL, 0, NULL, NULL);
97         multibyte->resize(size_needed);
98         WideCharToMultiByte(CP_UTF8, 0, wide->data(), (int)wide->size(), (char*)multibyte->data(), size_needed, NULL, NULL);
99     }
100 }
101 
102 // convert Windows Unicode to UTF-8
utf16ToUtf8(const wchar_t * utf16data,int utf16size,string * utf8string)103 void utf16ToUtf8(const wchar_t* utf16data, int utf16size, string* utf8string)
104 {
105     if(!utf16size)
106     {
107         utf8string->clear();
108         return;
109     }
110 
111     utf8string->resize((utf16size + 1) * 4);
112 
113     utf8string->resize(WideCharToMultiByte(CP_UTF8, 0, utf16data,
114         utf16size,
115         (char*)utf8string->data(),
116         int(utf8string->size() + 1),
117         NULL, NULL));
118 }
119 
getutf8fromUtf16(const wchar_t * ws)120 std::string getutf8fromUtf16(const wchar_t *ws)
121 {
122     string utf8s;
123     utf16ToUtf8(ws, wcslen(ws), &utf8s);
124     return utf8s;
125 }
126 
127 #endif
128 
129 
canWrite(string path)130 bool canWrite(string path)
131 {
132 #ifdef _WIN32
133     // TODO: Check permissions
134     return true;
135 #else
136     if (access(path.c_str(), W_OK) == 0)
137     {
138         return true;
139     }
140     return false;
141 #endif
142 }
143 
isPublicLink(string link)144 bool isPublicLink(string link)
145 {
146     //Old format:
147     //https://mega.nz/#!ph!key
148     //https://mega.nz/#F!ph!key
149 
150     //new format:
151     //https://mega.nz/file/ph#key
152     //https://mega.nz/folder/ph#key
153     if (( link.find("http") == 0 ) && ( link.find("#") != string::npos || link.find("/file/") != string::npos || link.find("/folder/") != string::npos))
154     {
155         return true;
156     }
157     return false;
158 }
159 
isEncryptedLink(string link)160 bool isEncryptedLink(string link)
161 {
162     if (( link.find("http") == 0 ) && ( link.find("#") != string::npos ) && (link.substr(link.find("#"),3) == "#P!") )
163     {
164         return true;
165     }
166     return false;
167 }
168 
getPublicLinkHandle(const string & link)169 string getPublicLinkHandle(const string &link)
170 {
171     size_t posFolder = string::npos;
172     size_t posLastSep = link.rfind("?");
173     if (posLastSep == string::npos )
174     {
175         string rest = link;
176         int count = 0;
177         size_t posExc = rest.find_first_of("!");
178         while ( posExc != string::npos && (posExc +1) < rest.size())
179         {
180             count++;
181             if (count <= 3 )
182             {
183                 posLastSep += posExc + 1;
184             }
185 
186             rest = rest.substr(posExc + 1);
187             posExc = rest.find("!");
188         }
189 
190         if (count != 3)
191         {
192             posLastSep = string::npos;
193         }
194     }
195 
196     if (posLastSep == string::npos )
197     {
198         posFolder = link.find("/folder/");
199     }
200 
201     if (posFolder != string::npos)
202     {
203         posLastSep = link.rfind("/file/");
204         if (posLastSep != string::npos)
205         {
206             posLastSep += strlen("/file/")-1;
207         }
208         else
209         {
210             posLastSep = link.rfind("/folder/");
211             if (posLastSep != string::npos && posFolder != posLastSep)
212             {
213                 posLastSep += strlen("/folder/")-1;
214             }
215             else
216             {
217                 return string();
218             }
219         }
220     }
221 
222     if (( posLastSep == string::npos ) || !( posLastSep + 1 < link.length()))
223     {
224         return string();
225     }
226     else
227     {
228         return link.substr(posLastSep+1);
229     }
230 }
231 
hasWildCards(string & what)232 bool hasWildCards(string &what)
233 {
234     return what.find('*') != string::npos || what.find('?') != string::npos;
235 }
236 
charstoll(const char * instr)237 long long charstoll(const char *instr)
238 {
239   long long retval;
240 
241   retval = 0;
242   for (; *instr; instr++) {
243     retval = 10*retval + (*instr - '0');
244   }
245   return retval;
246 }
247 
ltrim(std::string & s,const char & c)248 std::string &ltrim(std::string &s, const char &c)
249 {
250     size_t pos = s.find_first_not_of(c);
251     s = s.substr(pos == string::npos ? s.length() : pos, s.length());
252     return s;
253 }
254 
rtrim(std::string & s,const char & c)255 std::string &rtrim(std::string &s, const char &c)
256 {
257     size_t pos = s.find_last_of(c);
258     size_t last = pos == string::npos ? s.length() : pos;
259     if (last + 1 < s.length())
260     {
261         if (s.at(last + 1) != c)
262         {
263             last = s.length();
264         }
265     }
266 
267     s = s.substr(0, last);
268     return s;
269 }
270 
removeTrailingSeparators(string & path)271 string removeTrailingSeparators(string &path)
272 {
273     return rtrim(rtrim(path,'/'),'\\');
274 }
275 
getlistOfWords(char * ptr,bool escapeBackSlashInCompletion,bool ignoreTrailingSpaces)276 vector<string> getlistOfWords(char *ptr, bool escapeBackSlashInCompletion, bool ignoreTrailingSpaces)
277 {
278     vector<string> words;
279 
280     char* wptr;
281 
282     // split line into words with quoting and escaping
283     for (;; )
284     {
285         // skip leading blank space
286         while (*(const signed char*)ptr > 0 && *ptr <= ' ' && (ignoreTrailingSpaces || *(ptr+1)))
287         {
288             ptr++;
289         }
290 
291         if (!*ptr)
292         {
293             break;
294         }
295 
296         // quoted arg / regular arg
297         if (*ptr == '"')
298         {
299             ptr++;
300             wptr = ptr;
301             words.push_back(string());
302 
303             for (;; )
304             {
305                 if (( *ptr == '"' ) || ( *ptr == '\\' ) || !*ptr)
306                 {
307                     words[words.size() - 1].append(wptr, ptr - wptr);
308 
309                     if (!*ptr || ( *ptr++ == '"' ))
310                     {
311                         break;
312                     }
313 
314                     wptr = ptr - 1;
315                 }
316                 else
317                 {
318                     ptr++;
319                 }
320             }
321         }
322         else if (*ptr == '\'') // quoted arg / regular arg
323         {
324             ptr++;
325             wptr = ptr;
326             words.push_back(string());
327 
328             for (;; )
329             {
330                 if (( *ptr == '\'' ) || ( *ptr == '\\' ) || !*ptr)
331                 {
332                     words[words.size() - 1].append(wptr, ptr - wptr);
333 
334                     if (!*ptr || ( *ptr++ == '\'' ))
335                     {
336                         break;
337                     }
338 
339                     wptr = ptr - 1;
340                 }
341                 else
342                 {
343                     ptr++;
344                 }
345             }
346         }
347         else
348         {
349             while (*ptr == ' ') ptr++;// only possible if ptr+1 is the end
350 
351             wptr = ptr;
352 
353             char *prev = ptr;
354             //while ((unsigned char)*ptr > ' ')
355             while ((*ptr != '\0') && !(*ptr ==' ' && *prev !='\\'))
356             {
357                 if (*ptr == '"')
358                 {
359                     while (*++ptr != '"' && *ptr != '\0')
360                     { }
361                 }
362                 prev=ptr;
363                 ptr++;
364             }
365                 string newword(wptr, ptr - wptr);
366                 words.push_back(newword);
367         }
368     }
369 
370     if (escapeBackSlashInCompletion && words.size()> 1 && words[0] == "completion")
371     {
372         for (int i = 1; i < (int)words.size(); i++)
373         {
374             replaceAll(words[i],"\\","\\\\");
375         }
376     }
377 
378     return words;
379 }
380 
stringcontained(const char * s,vector<string> list)381 bool stringcontained(const char * s, vector<string> list)
382 {
383     for (int i = 0; i < (int)list.size(); i++)
384     {
385         if (list[i] == s)
386         {
387             return true;
388         }
389     }
390 
391     return false;
392 }
393 
dupstr(char * s)394 char * dupstr(char* s)
395 {
396     char *r;
397 
398     r = (char*)malloc(sizeof( char ) * ( strlen(s) + 1 ));
399     strcpy(r, s);
400     return( r );
401 }
402 
replace(std::string & str,const std::string & from,const std::string & to)403 bool replace(std::string& str, const std::string& from, const std::string& to)
404 {
405     size_t start_pos = str.find(from);
406     if (start_pos == std::string::npos)
407     {
408         return false;
409     }
410     str.replace(start_pos, from.length(), to);
411     return true;
412 }
413 
replaceAll(std::string & str,const std::string & from,const std::string & to)414 void replaceAll(std::string& str, const std::string& from, const std::string& to)
415 {
416     if (from.empty())
417     {
418         return;
419     }
420     size_t start_pos = 0;
421     while (( start_pos = str.find(from, start_pos)) != std::string::npos)
422     {
423         str.replace(start_pos, from.length(), to);
424         start_pos += to.length();
425     }
426 }
427 
toInteger(string what,int failValue)428 int toInteger(string what, int failValue)
429 {
430     if (what.empty())
431     {
432         return failValue;
433     }
434     if (!isdigit(what[0]) && !( what[0] != '-' ) && ( what[0] != '+' ))
435     {
436         return failValue;
437     }
438 
439     char * p;
440     long l = strtol(what.c_str(), &p, 10);
441 
442     if (*p != 0)
443     {
444         return failValue;
445     }
446 
447     if (( l < INT_MIN ) || ( l > INT_MAX ))
448     {
449         return failValue;
450     }
451     return (int)l;
452 }
453 
joinStrings(const vector<string> & vec,const char * delim,bool quoted)454 string joinStrings(const vector<string>& vec, const char* delim, bool quoted)
455 {
456     stringstream res;
457     if (!quoted)
458     {
459         std:copy(vec.begin(), vec.end(), ostream_iterator<string>(res, delim));
460     }
461     else
462     {
463         for(vector<string>::const_iterator i = vec.begin(); i != vec.end(); ++i)
464         {
465             res << "\"" << *i << "\"" << delim;
466         }
467     }
468     if (vec.size()>1)
469     {
470         string toret = res.str();
471         return toret.substr(0,toret.size()-strlen(delim));
472     }
473     return res.str();
474 }
475 
getstringutf8size(const string & str)476 unsigned int getstringutf8size(const string &str) {
477     int c,i,ix,q;
478     for (q=0, i=0, ix=int(str.length()); i < ix; i++, q++)
479     {
480         c = (unsigned char) str[i];
481 
482         if (c>=0 && c<=127) i+=0;
483         else if ((c & 0xE0) == 0xC0) i+=1;
484 #ifdef _WIN32
485         else if ((c & 0xF0) == 0xE0) i+=2;
486 #else
487         else if ((c & 0xF0) == 0xE0)
488         {
489             if ((i+2)>ix || c != 0xE2 || (strncmp(&str.c_str()[i],"\u21f5",3)
490                     && strncmp(&str.c_str()[i],"\u21d3",3) && strncmp(&str.c_str()[i],"\u21d1",3) ) )
491             { //known 1 character gliphs
492                 q++;
493             }
494             i+=2;
495         } //these gliphs may occupy 2 characters! Problem: not always. Let's assume the worse
496 #endif
497         else if ((c & 0xF8) == 0xF0) i+=3;
498         else return 0;//invalid utf8
499     }
500     return q;
501 }
502 
getFixLengthString(const string & origin,unsigned int size,const char delim,bool alignedright)503 string getFixLengthString(const string &origin, unsigned int size, const char delim, bool alignedright)
504 {
505     string toret;
506     size_t printableSize = getstringutf8size(origin);
507     size_t bytesSize = origin.size();
508     if (printableSize <= size){
509         if (alignedright)
510         {
511             toret.insert(0,size-printableSize,delim);
512             toret.insert(size-bytesSize,origin,0,bytesSize);
513 
514         }
515         else
516         {
517             toret.insert(0,origin,0,bytesSize);
518             toret.insert(bytesSize,size-printableSize,delim);
519         }
520     }
521     else
522     {
523         toret.insert(0,origin,0,(size+1)/2-2);
524         if (size > 3) toret.insert((size+1)/2-2,3,'.');
525         if (size > 1) toret.insert((size+1)/2+1,origin,bytesSize-(size)/2+1,(size)/2-1); //TODO: This could break characters if multibyte!  //alternative: separate in multibyte strings and print one by one?
526     }
527 
528     return toret;
529 }
530 
getRightAlignedString(const string origin,unsigned int minsize)531 string getRightAlignedString(const string origin, unsigned int minsize)
532 {
533     ostringstream os;
534     os << std::setw(minsize) << origin;
535     return os.str();
536 }
537 
printCenteredLine(OUTSTREAMTYPE & os,string msj,unsigned int width,bool encapsulated)538 void printCenteredLine(OUTSTREAMTYPE &os, string msj, unsigned int width, bool encapsulated)
539 {
540     unsigned int msjsize = getstringutf8size(msj);
541     bool overflowed = false;
542     if (msjsize>width)
543     {
544         overflowed = true;
545         width = unsigned(msjsize);
546     }
547     if (encapsulated && !overflowed)
548         os << "|";
549     for (unsigned int i = 0; i < (width-msjsize)/2; i++)
550         os << " ";
551     os << msj;
552     for (unsigned int i = 0; i < (width-msjsize)/2 + (width-msjsize)%2 ; i++)
553         os << " ";
554     if (encapsulated && !overflowed)
555         os << "|";
556     os << endl;
557 }
558 
printCenteredContents(OUTSTREAMTYPE & os,string msj,unsigned int width,bool encapsulated)559 void printCenteredContents(OUTSTREAMTYPE &os, string msj, unsigned int width, bool encapsulated)
560 {
561      string headfoot = " ";
562      headfoot.append(width, '-');
563      unsigned int msjsize = getstringutf8size(msj);
564 
565      bool printfooter = false;
566 
567      if (msj.size())
568      {
569          string header;
570          if (msj.at(0) == '<')
571          {
572              size_t possenditle = msj.find(">");
573              if (width >= 2 && possenditle < (width -2))
574              {
575                  header.append(" ");
576                  header.append((width - possenditle ) / 2, '-');
577                  header.append(msj.substr(0,possenditle+1));
578                  header.append(width - getstringutf8size(header) + 1, '-');
579                  msj = msj.substr(possenditle + 1);
580              }
581          }
582          if (header.size() || encapsulated)
583          {
584              os << (header.size()?header:headfoot) << endl;
585              printfooter = true;
586          }
587      }
588 
589      size_t possepnewline = msj.find("\n");
590      size_t possep = msj.find(" ");
591 
592      if (possepnewline != string::npos && possepnewline < width)
593      {
594          possep = possepnewline;
595      }
596      size_t possepprev = possep;
597 
598 
599      while (msj.size())
600      {
601 
602          if (possepnewline != string::npos && possepnewline <= width)
603          {
604              possep = possepnewline;
605              possepprev = possep;
606          }
607          else
608          {
609              while (possep < width && possep != string::npos)
610              {
611                  possepprev = possep;
612                  possep = msj.find_first_of(" ", possep+1);
613              }
614          }
615 
616          if (possepprev == string::npos || (possep == string::npos && msj.size() <= width))
617          {
618              printCenteredLine(os, msj, width, encapsulated);
619              break;
620          }
621          else
622          {
623              printCenteredLine(os, msj.substr(0,possepprev), width, encapsulated);
624              if (possepprev < (msj.size() - 1))
625              {
626                  msj = msj.substr(possepprev + 1);
627                  possepnewline = msj.find("\n");
628                  possep = msj.find(" ");
629                  possepprev = possep;
630              }
631              else
632              {
633                  break;
634              }
635          }
636      }
637      if (printfooter)
638      {
639          os << headfoot << endl;
640      }
641 }
642 
printCenteredLine(string msj,unsigned int width,bool encapsulated)643 void printCenteredLine(string msj, unsigned int width, bool encapsulated)
644 {
645     OUTSTRINGSTREAM os;
646     printCenteredLine(os, msj, width, encapsulated);
647     COUT << os.str();
648 }
649 
printCenteredContents(string msj,unsigned int width,bool encapsulated)650 void printCenteredContents(string msj, unsigned int width, bool encapsulated)
651 {
652     OUTSTRINGSTREAM os;
653     printCenteredContents(os, msj, width, encapsulated);
654     COUT << os.str();
655 }
656 
printCenteredContentsCerr(string msj,unsigned int width,bool encapsulated)657 void printCenteredContentsCerr(string msj, unsigned int width, bool encapsulated)
658 {
659     OUTSTRINGSTREAM os;
660     printCenteredContents(os, msj, width, encapsulated);
661     CERR << os.str();
662 }
663 
printPercentageLineCerr(const char * title,long long completed,long long total,float percentDowloaded,bool cleanLineAfter)664 void printPercentageLineCerr(const char *title, long long completed, long long total, float percentDowloaded, bool cleanLineAfter)
665 {
666     int cols = getNumberOfCols(80);
667 
668     string outputString;
669     outputString.resize(cols + 1);
670     for (int i = 0; i < cols; i++)
671     {
672         outputString[i] = '.';
673     }
674 
675     outputString[cols] = '\0';
676     char *ptr = (char *)outputString.c_str();
677     sprintf(ptr, "%s%s", title, " ||");
678     ptr += strlen(title);
679     ptr += strlen(" ||");
680     *ptr = '.'; //replace \0 char
681 
682     char aux[41];
683 
684     if (total < 1048576)
685     {
686         sprintf(aux,"||(%lld/%lld KB: %6.2f %%) ", completed / 1024, total / 1024, percentDowloaded);
687     }
688     else
689     {
690         sprintf(aux,"||(%lld/%lld MB: %6.2f %%) ", completed / 1024 / 1024, total / 1024 / 1024, percentDowloaded);
691     }
692 
693 
694     sprintf((char *)outputString.c_str() + cols - strlen(aux), "%s",                         aux);
695     for (int i = 0; i < ( cols - (strlen(title) + strlen(" ||")) - strlen(aux)) * 1.0 * min(100.0f,percentDowloaded) / 100.0; i++)
696     {
697         *ptr++ = '#';
698     }
699 
700     if (cleanLineAfter)
701     {
702         cerr << outputString << '\r' << flush;
703     }
704     else
705     {
706         cerr << outputString << endl;
707     }
708 }
709 
getFlag(map<string,int> * flags,const char * optname)710 int getFlag(map<string, int> *flags, const char * optname)
711 {
712     return flags->count(optname) ? ( *flags )[optname] : 0;
713 }
714 
getOption(map<string,string> * cloptions,const char * optname,string defaultValue)715 string getOption(map<string, string> *cloptions, const char * optname, string defaultValue)
716 {
717     return cloptions->count(optname) ? ( *cloptions )[optname] : defaultValue;
718 }
719 
getintOption(map<string,string> * cloptions,const char * optname,int defaultValue)720 int getintOption(map<string, string> *cloptions, const char * optname, int defaultValue)
721 {
722     if (cloptions->count(optname))
723     {
724         int i = defaultValue;
725         istringstream is(( *cloptions )[optname]);
726         is >> i;
727         return i;
728     }
729     else
730     {
731         return defaultValue;
732     }
733 }
734 
discardOptionsAndFlags(vector<string> * ws)735 void discardOptionsAndFlags(vector<string> *ws)
736 {
737     for (std::vector<string>::iterator it = ws->begin(); it != ws->end(); )
738     {
739         /* std::cout << *it; ... */
740         string w = ( string ) * it;
741         if (w.length() && ( w.at(0) == '-' )) //begins with "-"
742         {
743             it = ws->erase(it);
744         }
745         else //not an option/flag
746         {
747             ++it;
748         }
749     }
750 }
751 
sizeProgressToText(long long partialSize,long long totalSize,bool equalizeUnitsLength,bool humanreadable)752 string sizeProgressToText(long long partialSize, long long totalSize, bool equalizeUnitsLength, bool humanreadable)
753 {
754     ostringstream os;
755     os.precision(2);
756     if (humanreadable)
757     {
758         string unit;
759         unit = ( equalizeUnitsLength ? " B" : "B" );
760         double reducedPartSize = (double)totalSize;
761         double reducedSize = (double)totalSize;
762 
763         if ( totalSize > 1099511627776LL *2 )
764         {
765             reducedPartSize = totalSize / (double) 1099511627776ull;
766             reducedSize = totalSize / (double) 1099511627776ull;
767             unit = "TB";
768         }
769         else if ( totalSize > 1073741824LL *2 )
770         {
771             reducedPartSize = totalSize / (double) 1073741824L;
772             reducedSize = totalSize / (double) 1073741824L;
773             unit = "GB";
774         }
775         else if (totalSize > 1048576 * 2)
776         {
777             reducedPartSize = totalSize / (double) 1048576;
778             reducedSize = totalSize / (double) 1048576;
779             unit = "MB";
780         }
781         else if (totalSize > 1024 * 2)
782         {
783             reducedPartSize = totalSize / (double) 1024;
784             reducedSize = totalSize / (double) 1024;
785             unit = "KB";
786         }
787         os << fixed << reducedPartSize << "/" << reducedSize;
788         os << " " << unit;
789     }
790     else
791     {
792         os << partialSize << "/" << totalSize;
793     }
794 
795     return os.str();
796 }
797 
sizeToText(long long totalSize,bool equalizeUnitsLength,bool humanreadable)798 string sizeToText(long long totalSize, bool equalizeUnitsLength, bool humanreadable)
799 {
800     ostringstream os;
801     os.precision(2);
802     if (humanreadable)
803     {
804         string unit;
805         unit = ( equalizeUnitsLength ? " B" : "B" );
806         double reducedSize = (double)totalSize;
807 
808         if ( totalSize > 1099511627776LL *2 )
809         {
810             reducedSize = totalSize / (double) 1099511627776ull;
811             unit = "TB";
812         }
813         else if ( totalSize > 1073741824LL *2 )
814         {
815             reducedSize = totalSize / (double) 1073741824L;
816             unit = "GB";
817         }
818         else if (totalSize > 1048576 * 2)
819         {
820             reducedSize = totalSize / (double) 1048576;
821             unit = "MB";
822         }
823         else if (totalSize > 1024 * 2)
824         {
825             reducedSize = totalSize / (double) 1024;
826             unit = "KB";
827         }
828         os << fixed << reducedSize;
829         os << " " << unit;
830     }
831     else
832     {
833         os << totalSize;
834     }
835 
836     return os.str();
837 }
838 
textToSize(const char * text)839 int64_t textToSize(const char *text)
840 {
841     int64_t sizeinbytes = 0;
842 
843     char * ptr = (char *)text;
844     char * last = (char *)text;
845     while (*ptr != '\0')
846     {
847         if (( *ptr < '0' ) || ( *ptr > '9' ) || ( *ptr == '.' ) )
848         {
849             switch (*ptr)
850             {
851                 case 'b': //Bytes
852                 case 'B':
853                     *ptr = '\0';
854                     sizeinbytes += int64_t(atof(last));
855                     break;
856 
857                 case 'k': //KiloBytes
858                 case 'K':
859                     *ptr = '\0';
860                     sizeinbytes += int64_t(1024.0 * atof(last));
861                     break;
862 
863                 case 'm': //MegaBytes
864                 case 'M':
865                     *ptr = '\0';
866                     sizeinbytes += int64_t(1048576.0 * atof(last));
867                     break;
868 
869                 case 'g': //GigaBytes
870                 case 'G':
871                     *ptr = '\0';
872                     sizeinbytes += int64_t(1073741824.0 * atof(last));
873                     break;
874 
875                 case 't': //TeraBytes
876                 case 'T':
877                     *ptr = '\0';
878                     sizeinbytes += int64_t(1125899906842624.0 * atof(last));
879                     break;
880 
881                 default:
882                 {
883                     return -1;
884                 }
885             }
886             last = ptr + 1;
887         }
888         char *prev = ptr;
889         ptr++;
890         if (*ptr == '\0' && ( ( *prev == '.' ) || ( ( *prev >= '0' ) && ( *prev <= '9' ) ) ) ) //reach the end with a number or dot
891         {
892             return -1;
893         }
894     }
895     return sizeinbytes;
896 
897 }
898 
percentageToText(float percentage)899 string percentageToText(float percentage)
900 {
901     ostringstream os;
902     os.precision(2);
903     if (percentage != percentage) //NaN
904     {
905         os << "----%";
906     }
907     else
908     {
909         os << fixed << percentage*100.0 << "%";
910     }
911 
912     return os.str();
913 }
914 
getNumberOfCols(unsigned int defaultwidth)915 unsigned int getNumberOfCols(unsigned int defaultwidth)
916 {
917 #ifdef _WIN32
918     CONSOLE_SCREEN_BUFFER_INFO csbi;
919     int columns = defaultwidth;
920 
921     if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
922     {
923         columns = csbi.srWindow.Right - csbi.srWindow.Left - 1;
924     }
925 
926     return columns;
927 #else
928     struct winsize size;
929     if ( ioctl(STDOUT_FILENO,TIOCGWINSZ,&size) != -1
930          || (ioctl(STDIN_FILENO,TIOCGWINSZ,&size) != -1))
931     {
932         if (size.ws_col > 2)
933         {
934             return size.ws_col - 2;
935         }
936     }
937 #endif
938     return defaultwidth;
939 }
940 
sleepSeconds(int seconds)941 void sleepSeconds(int seconds)
942 {
943 #ifdef _WIN32
944     Sleep(1000*seconds);
945 #else
946     sleep(seconds);
947 #endif
948 }
949 
sleepMilliSeconds(long milliseconds)950 void sleepMilliSeconds(long milliseconds)
951 {
952 #ifdef _WIN32
953     Sleep(milliseconds);
954 #else
955     usleep(milliseconds *1000);
956 #endif
957 }
958 
isValidEmail(string email)959 bool isValidEmail(string email)
960 {
961     return !( (email.find("@") == string::npos)
962                     || (email.find_last_of(".") == string::npos)
963                     || (email.find("@") > email.find_last_of(".")));
964 }
965 
966 #ifdef __linux__
getCurrentExecPath()967 std::string getCurrentExecPath()
968 {
969     std::string path = ".";
970     pid_t pid = getpid();
971     char buf[20] = {0};
972     sprintf(buf,"%d",pid);
973     std::string _link = "/proc/";
974     _link.append( buf );
975     _link.append( "/exe");
976     char proc[PATH_MAX];
977     int ch = readlink(_link.c_str(),proc,PATH_MAX);
978     if (ch != -1) {
979         proc[ch] = 0;
980         path = proc;
981         std::string::size_type t = path.find_last_of("/");
982         path = path.substr(0,t);
983     }
984 
985     return path;
986 }
987 #endif
988 
ltrimProperty(string & s,const char & c)989 string &ltrimProperty(string &s, const char &c)
990 {
991     size_t pos = s.find_first_not_of(c);
992     s = s.substr(pos == string::npos ? s.length() : pos, s.length());
993     return s;
994 }
995 
rtrimProperty(string & s,const char & c)996 string &rtrimProperty(string &s, const char &c)
997 {
998     size_t pos = s.find_last_not_of(c);
999     if (pos != string::npos)
1000     {
1001         pos++;
1002     }
1003     s = s.substr(0, pos);
1004     return s;
1005 }
1006 
trimProperty(string & what)1007 string &trimProperty(string &what)
1008 {
1009     rtrimProperty(what,' ');
1010     ltrimProperty(what,' ');
1011     if (what.size() > 1)
1012     {
1013         if (what[0] == '\'' || what[0] == '"')
1014         {
1015             rtrimProperty(what, what[0]);
1016             ltrimProperty(what, what[0]);
1017         }
1018     }
1019     return what;
1020 }
1021 
getPropertyFromFile(const char * configFile,const char * propertyName)1022 string getPropertyFromFile(const char *configFile, const char *propertyName)
1023 {
1024     ifstream infile(configFile);
1025     string line;
1026 
1027     while (getline(infile, line))
1028     {
1029         if (line.length() > 0 && line[0] != '#')
1030         {
1031             if (!strlen(propertyName)) //if empty return first line
1032             {
1033                 return trimProperty(line);
1034             }
1035             string key, value;
1036             size_t pos = line.find("=");
1037             if (pos != string::npos && ((pos + 1) < line.size()))
1038             {
1039                 key = line.substr(0, pos);
1040                 rtrimProperty(key, ' ');
1041 
1042                 if (!strcmp(key.c_str(), propertyName))
1043                 {
1044                     value = line.substr(pos + 1);
1045                     return trimProperty(value);
1046                 }
1047             }
1048         }
1049     }
1050 
1051     return string();
1052 }
1053 
endregistry()1054 void ColumnDisplayer::endregistry()
1055 {
1056     values.push_back(std::move(currentRegistry));
1057     currentlength = 0;
1058 }
1059 
addHeader(const string & name,bool fixed,int minWidth)1060 void ColumnDisplayer::addHeader(const string &name, bool fixed, int minWidth)
1061 {
1062     fields[name] = Field(name, fixed, minWidth);
1063 }
1064 
addValue(const string & name,const string & value,bool replace)1065 void ColumnDisplayer::addValue(const string &name, const string &value, bool replace)
1066 {
1067     int len = getstringutf8size(value);
1068     if (!replace)
1069     {
1070         if (currentRegistry.size() && currentRegistry.find(name) != currentRegistry.end())
1071         {
1072             endregistry();
1073         }
1074     }
1075 
1076     currentRegistry[name] = value;
1077     currentlength += len;
1078     if (fields.find(name) == fields.end())
1079     {
1080         addHeader(name, true);
1081     }
1082     if (find (fieldnames.begin(), fieldnames.end(), name) == fieldnames.end())
1083     {
1084         fieldnames.push_back(name);
1085     }
1086 
1087     fields[name].updateMaxValue(len);
1088 }
1089 
ColumnDisplayer(int unfixedColsMinSize)1090 ColumnDisplayer::ColumnDisplayer(int unfixedColsMinSize) : mUnfixedColsMinSize(unfixedColsMinSize)
1091 {
1092 
1093 }
1094 
print(OUTSTREAMTYPE & os,int fullWidth,bool printHeader)1095 void ColumnDisplayer::print(OUTSTREAMTYPE &os, int fullWidth, bool printHeader)
1096 {
1097     if (currentRegistry.size())
1098     {
1099         endregistry();
1100     }
1101 
1102     int unfixedfieldscount = 0;
1103     int unfixedFieldsMaxLengthSum = 0;
1104 
1105     int leftWidth = fullWidth;
1106     vector<Field *> unfixedfields;
1107     for (auto &el : fields)
1108     {
1109         Field &f = el.second;
1110         if (f.fixedSize)
1111 
1112         {
1113             if (f.fixedWidth)
1114             {
1115                 f.dispWidth = f.fixedWidth;
1116             }
1117             else
1118             {
1119                 f.dispWidth = max((int)getstringutf8size(f.name),f.maxValueLength);
1120             }
1121             leftWidth-=(f.dispWidth + 1);
1122         }
1123         else
1124         {
1125             unfixedfieldscount++;
1126             unfixedfields.push_back(&f);
1127             unfixedFieldsMaxLengthSum+=f.maxValueLength;
1128         }
1129     }
1130 
1131     auto unfixedFieldsMaxLengthsLeft = unfixedFieldsMaxLengthSum;
1132     for (auto &f: unfixedfields)
1133     {
1134         unfixedFieldsMaxLengthsLeft -= f->maxValueLength;
1135 
1136         f->dispWidth = max(
1137                     (int)getstringutf8size(f->name), // min limit: header size
1138                     min( f->maxValueLength // max limit: its longest value
1139                          , max(mUnfixedColsMinSize, // min limit 2: the min limit for unfixed columns
1140                                max((leftWidth - unfixedfieldscount + 1)/(unfixedfieldscount), (leftWidth - unfixedfieldscount + 1 - unfixedFieldsMaxLengthsLeft)) //either an equitative share between all unfixedfields left, or all the space the other left me considering their maxLegnths
1141                                )
1142                         )
1143                     );
1144         leftWidth-=(f->dispWidth + 1);
1145         unfixedfieldscount--;
1146     }
1147 
1148     if (printHeader)
1149     {
1150         bool first = true;
1151         for (auto el : fieldnames)
1152         {
1153             Field &f = fields[el];
1154             if (!first)
1155             {
1156                 os << " ";
1157             }
1158             first = false;
1159             os << getFixLengthString(f.name, f.dispWidth);
1160         }
1161     }
1162     os << std::endl;
1163 
1164     for (auto &registry : values)
1165     {
1166         bool firstvalue = true;
1167         for (auto &el : fieldnames)
1168         {
1169             Field &f = fields[el];
1170             if (!firstvalue)
1171             {
1172                 os << " ";
1173             }
1174             firstvalue = false;
1175 
1176             if (registry.find(f.name) != registry.end())
1177             {
1178                 os << getFixLengthString(registry[f.name], f.dispWidth);
1179             }
1180             else
1181             {
1182                 os << getFixLengthString("", f.dispWidth);
1183             }
1184 
1185         }
1186         os << std::endl;
1187     }
1188 }
1189 
Field()1190 Field::Field()
1191 {
1192 
1193 }
1194 
Field(string name,bool fixed,int minWidth)1195 Field::Field(string name, bool fixed, int minWidth) :name(name), fixedSize(fixed), fixedWidth(minWidth)
1196 {
1197     if (fixed)
1198     {
1199         this->dispWidth = minWidth;
1200     }
1201 
1202 }
1203 
updateMaxValue(int newcandidate)1204 void Field::updateMaxValue(int newcandidate)
1205 {
1206     if (newcandidate > this->maxValueLength)
1207     {
1208         this->maxValueLength = newcandidate;
1209     }
1210 }
1211 
1212 } //end namespace
1213