1 //////////////////////////////////////////////////////////////////////////
2 //
3 // pgAdmin III - PostgreSQL Tools
4 //
5 // Copyright (C) 2002 - 2016, The pgAdmin Development Team
6 // This software is released under the PostgreSQL Licence
7 //
8 // misc.cpp - Miscellaneous Utilities
9 //
10 //////////////////////////////////////////////////////////////////////////
11
12 #include "pgAdmin3.h"
13
14 // wxWindows headers
15 #include <wx/wx.h>
16 #include <wx/app.h>
17 #include <wx/timer.h>
18 #include <wx/xrc/xmlres.h>
19 #include <wx/dir.h>
20 #include <wx/file.h>
21 #include <wx/textbuf.h>
22 #include <wx/help.h>
23 #include <wx/html/helpctrl.h>
24 #include <wx/fontenc.h>
25 #include <wx/display.h>
26
27 #include "utils/utffile.h"
28 #include <locale.h>
29
30 #ifdef __WXMSW__
31 #include <wx/msw/helpchm.h>
32 #endif
33
34 // Standard headers
35 #include <stdlib.h>
36
37 // App headers
38 #include "utils/misc.h"
39
40 #if !defined(PGSCLI)
41
42 // App headers
43 #include "frm/frmMain.h"
44
45 wxImageList *imageList = 0;
46
47 #endif // PGSCLI
48
49 extern "C"
50 {
51 #define YYSTYPE_IS_DECLARED
52 #define DECIMAL DECIMAL_P
53 typedef int YYSTYPE;
54 #include "parser/keywords.h"
55 }
56
57 // we dont have an appropriate wxLongLong method
58 #ifdef __WIN32__
59 #define atolonglong _atoi64
60 #else
61 #ifdef __WXMAC__
62 #define atolonglong(str) strtoll(str, (char **)NULL, 10)
63 #else
64 #ifdef __FreeBSD__
65 #define atolonglong(str) strtoll(str, (char **)NULL, 10)
66 #else
67 #define atolonglong atoll
68 #endif
69 #endif
70 #endif
71
72
73
74 // Conversions
75
76
BoolToYesNo(bool value)77 wxString BoolToYesNo(bool value)
78 {
79 return value ? _("Yes") : _("No");
80 }
81
82
BoolToStr(bool value)83 wxString BoolToStr(bool value)
84 {
85 return value ? wxT("true") : wxT("false");
86 }
87
88
89
StrToBool(const wxString & value)90 bool StrToBool(const wxString &value)
91 {
92 if (value.StartsWith(wxT("t")))
93 {
94 return true;
95 }
96 else if (value.StartsWith(wxT("T")))
97 {
98 return true;
99 }
100 else if (value.StartsWith(wxT("1")))
101 {
102 return true;
103 }
104 else if (value.StartsWith(wxT("Y")))
105 {
106 return true;
107 }
108 else if (value.StartsWith(wxT("y")))
109 {
110 return true;
111 }
112 else if (value == wxT("on"))
113 return true;
114
115 return false;
116 }
117
NumToStr(long value)118 wxString NumToStr(long value)
119 {
120 wxString result;
121 result.Printf(wxT("%ld"), value);
122 return result;
123 }
124
125
NumToStr(OID value)126 wxString NumToStr(OID value)
127 {
128 wxString result;
129 result.Printf(wxT("%lu"), (long)value);
130 return result;
131 }
132
133
StrToLong(const wxString & value)134 long StrToLong(const wxString &value)
135 {
136 return atol(value.ToAscii());
137 }
138
139
StrToOid(const wxString & value)140 OID StrToOid(const wxString &value)
141 {
142 return (OID)strtoul(value.ToAscii(), 0, 10);
143 }
144
generate_spaces(int length)145 wxString generate_spaces(int length)
146 {
147 return wxString().Pad(length);
148 }
149
NumToStr(double value)150 wxString NumToStr(double value)
151 {
152 wxString result;
153 static wxString decsep;
154
155 if (decsep.Length() == 0)
156 {
157 decsep.Printf(wxT("%lf"), 1.2);
158 decsep = decsep[(unsigned int)1];
159 }
160
161 result.Printf(wxT("%lf"), value);
162 result.Replace(decsep, wxT("."));
163
164 // Get rid of excessive decimal places
165 if (result.Contains(wxT(".")))
166 while (result.Right(1) == wxT("0"))
167 result.RemoveLast();
168 if (result.Right(1) == wxT("."))
169 result.RemoveLast();
170
171 return result;
172 }
173
174
NumToStr(wxLongLong value)175 wxString NumToStr(wxLongLong value)
176 {
177 wxString str;
178 #if wxCHECK_VERSION(2, 9, 0)
179 str.Printf("%" wxLongLongFmtSpec "d", value.GetValue());
180 #else
181 str.Printf(wxT("%") wxLongLongFmtSpec wxT("d"), value.GetValue());
182 #endif
183 return str;
184 }
185
186
StrToDouble(const wxString & value)187 double StrToDouble(const wxString &value)
188 {
189 wxCharBuffer buf = value.ToAscii();
190 char *p = (char *)strchr(buf, '.');
191 if (p)
192 *p = localeconv()->decimal_point[0];
193
194 return strtod(buf, 0);
195 }
196
197
StrToLongLong(const wxString & value)198 wxLongLong StrToLongLong(const wxString &value)
199 {
200 return atolonglong(value.ToAscii());
201 }
202
203
DateToAnsiStr(const wxDateTime & datetime)204 wxString DateToAnsiStr(const wxDateTime &datetime)
205 {
206 if (!datetime.IsValid())
207 return wxEmptyString;
208
209 return datetime.FormatISODate() + wxT(" ") + datetime.FormatISOTime();
210 }
211
212
DateToStr(const wxDateTime & datetime)213 wxString DateToStr(const wxDateTime &datetime)
214 {
215 if (!datetime.IsValid())
216 return wxEmptyString;
217
218 return datetime.FormatDate() + wxT(" ") + datetime.FormatTime();
219 }
220
221
ElapsedTimeToStr(wxLongLong msec)222 wxString ElapsedTimeToStr(wxLongLong msec)
223 {
224 wxTimeSpan tsMsec(0, 0, 0, msec);
225
226 int days = tsMsec.GetDays();
227 int hours = (wxTimeSpan(tsMsec.GetHours(), 0, 0, 0) - wxTimeSpan(days * 24)).GetHours();
228 int minutes = (wxTimeSpan(0, tsMsec.GetMinutes(), 0, 0) - wxTimeSpan(hours)).GetMinutes();
229 long seconds = (wxTimeSpan(0, 0, tsMsec.GetSeconds(), 0) - wxTimeSpan(hours, minutes)).GetSeconds().ToLong();
230 long milliseconds = (wxTimeSpan(0, 0, 0, tsMsec.GetMilliseconds()) - wxTimeSpan(0, 0, seconds)).GetMilliseconds().ToLong();
231
232 if (days > 0)
233 return wxString::Format(
234 wxT("%d %s, %02d:%02d:%02ld hours"),
235 days, wxT("days"), hours, minutes, seconds
236 );
237 else if (hours > 0)
238 return wxString::Format(
239 wxT("%02d:%02d:%02ld hours"), hours, minutes, seconds
240 );
241 else if (msec >= 1000 * 60)
242 return wxString::Format(wxT("%02d:%02ld minutes"), minutes, seconds);
243 else if (msec >= 1000)
244 return wxString::Format(
245 wxT("%ld.%ld secs"), seconds, milliseconds / 100
246 );
247 else
248 return msec.ToString() + wxT(" msec");
249 }
250
StrToDateTime(const wxString & value)251 wxDateTime StrToDateTime(const wxString &value)
252 {
253 wxDateTime dt;
254 /* This hasn't just been used. ( Is not infinity ) */
255 if ( !value.IsEmpty() )
256 dt.ParseDateTime(value);
257 return dt;
258 }
259
260 #if !defined(PGSCLI)
261
CheckOnScreen(wxWindow * win,wxPoint & pos,wxSize & size,const int w0,const int h0)262 void CheckOnScreen(wxWindow *win, wxPoint &pos, wxSize &size, const int w0, const int h0)
263 {
264 wxRect rect;
265 int scrH, scrW;
266
267 wxSize screenSize = wxGetDisplaySize();
268 scrW = screenSize.x;
269 scrH = screenSize.y;
270
271 if (pos.x > scrW - w0)
272 pos.x = scrW - w0;
273 if (pos.y > scrH - h0)
274 pos.y = scrH - h0;
275
276 if (pos.x < 0)
277 pos.x = 0;
278 if (pos.y < 0)
279 pos.y = 0;
280
281 if (size.GetWidth() < w0)
282 size.SetWidth(w0);
283 if (size.GetHeight() < h0)
284 size.SetHeight(h0);
285
286 if (size.GetWidth() > scrW)
287 size.SetWidth(scrW);
288 if (size.GetHeight() > scrH)
289 size.SetHeight(scrH);
290 }
291
292 #endif // PGSCLI
293
qtConnString(const wxString & value)294 wxString qtConnString(const wxString &value)
295 {
296 wxString result = value;
297
298 result.Replace(wxT("\\"), wxT("\\\\"));
299 result.Replace(wxT("'"), wxT("\\'"));
300 result.Append(wxT("'"));
301 result.Prepend(wxT("'"));
302
303 return result;
304 }
305
306 #if !defined(PGSCLI)
307
IdAndName(long id,const wxString & name)308 wxString IdAndName(long id, const wxString &name)
309 {
310 wxString str;
311 str.Printf(wxT("%ld - %s"), id, name.BeforeFirst('\n').c_str());
312 return str;
313 }
314
315
qtDbStringDollar(const wxString & value)316 wxString qtDbStringDollar(const wxString &value)
317 {
318 wxString qtDefault = wxT("BODY");
319 wxString qt = qtDefault;
320 int counter = 1;
321 if (value.Find('\'') < 0 && value.Find('\n') < 0 && value.Find('\r') < 0)
322 {
323 wxString ret = value;
324 ret.Replace(wxT("\\"), wxT("\\\\"));
325 ret.Replace(wxT("'"), wxT("''"));
326 ret.Append(wxT("'"));
327 ret.Prepend(wxT("'"));
328 return ret;
329 }
330
331 while (value.Find(wxT("$") + qt + wxT("$")) >= 0)
332 qt.Printf(wxT("%s%d"), qtDefault.c_str(), counter++);
333
334
335 return wxT("$") + qt + wxT("$")
336 + value
337 + wxT("$") + qt + wxT("$");
338 }
339
340
FillKeywords(wxString & str)341 void FillKeywords(wxString &str)
342 {
343 // unfortunately, the keyword list is static.
344 int i;
345
346 str = wxString();
347
348 for (i = 0; i < NumScanKeywords; i++)
349 {
350 str += wxT(" ") + wxString::FromAscii(ScanKeywords[i].name);
351 }
352 for (i = 0; i < NumScanKeywordsExtra; i++)
353 {
354 str += wxT(" ") + wxString::FromAscii(ScanKeywordsExtra[i].name);
355 }
356 }
357
358 #endif // PGSCLI
359
needsQuoting(wxString & value,bool forTypes)360 static bool needsQuoting(wxString &value, bool forTypes)
361 {
362 // Is it a number?
363 if (value.IsNumber())
364 return true;
365 else
366 {
367 // certain types should not be quoted even though it contains a space. Evilness.
368 wxString valNoArray;
369 if (forTypes && value.Right(2) == wxT("[]"))
370 valNoArray = value.Mid(0, value.Len() - 2);
371 else
372 valNoArray = value;
373
374 if (forTypes &&
375 (!valNoArray.CmpNoCase(wxT("character varying")) ||
376 !valNoArray.CmpNoCase(wxT("\"char\"")) ||
377 !valNoArray.CmpNoCase(wxT("bit varying")) ||
378 !valNoArray.CmpNoCase(wxT("double precision")) ||
379 !valNoArray.CmpNoCase(wxT("timestamp without time zone")) ||
380 !valNoArray.CmpNoCase(wxT("timestamp with time zone")) ||
381 !valNoArray.CmpNoCase(wxT("time without time zone")) ||
382 !valNoArray.CmpNoCase(wxT("time with time zone")) ||
383 !valNoArray.CmpNoCase(wxT("\"trigger\"")) ||
384 !valNoArray.CmpNoCase(wxT("\"unknown\""))))
385 return false;
386
387 int pos = 0;
388 while (pos < (int)valNoArray.length())
389 {
390 wxChar c = valNoArray.GetChar(pos);
391 if (!((pos > 0) && (c >= '0' && c <= '9')) &&
392 !(c >= 'a' && c <= 'z') &&
393 !(c == '_'))
394 {
395 return true;
396 }
397 pos++;
398 }
399 }
400
401 // is it a keyword?
402 const ScanKeyword *sk = ScanKeywordLookup(value.ToAscii());
403 if (!sk)
404 return false;
405 if (sk->category == UNRESERVED_KEYWORD)
406 return false;
407 if (forTypes && sk->category == COL_NAME_KEYWORD)
408 return false;
409 return true;
410 }
411
qtTypeIdent(const wxString & value)412 wxString qtTypeIdent(const wxString &value)
413 {
414 if (value.Length() == 0)
415 return value;
416
417 wxString result = value;
418
419 if (needsQuoting(result, true))
420 {
421 result.Replace(wxT("\""), wxT("\"\""));
422 return wxT("\"") + result + wxT("\"");
423 }
424 else
425 return result;
426 }
427
428
qtIdent(const wxString & value)429 wxString qtIdent(const wxString &value)
430 {
431 if (value.Length() == 0)
432 return value;
433
434 wxString result = value;
435
436 if (needsQuoting(result, false))
437 {
438 result.Replace(wxT("\""), wxT("\"\""));
439 return wxT("\"") + result + wxT("\"");
440 }
441 else
442 return result;
443 }
444
445 #if !defined(PGSCLI)
446
qtStrip(const wxString & str)447 wxString qtStrip(const wxString &str)
448 {
449 if (str.Left(2) == wxT("\\\""))
450 return str.Mid(2, str.Length() - 4);
451 else
452 return str;
453 }
454
455
TransformToNewDatconfig(const wxString & list)456 wxString TransformToNewDatconfig(const wxString &list)
457 {
458 const wxChar *cp = list.c_str();
459
460 wxString config = wxEmptyString;
461 wxString str = wxEmptyString;
462 bool quote = false;
463
464 while (*cp)
465 {
466 switch (*cp)
467 {
468 case '\\':
469 {
470 if (cp[1] == '"')
471 {
472 str.Append(*cp);
473 cp++;
474 str.Append(*cp);
475 }
476 break;
477 }
478 case '"':
479 case '\'':
480 {
481 quote = !quote;
482 str.Append(*cp);
483 break;
484 }
485 case ',':
486 {
487 if (!quote)
488 {
489 if (!config.IsEmpty())
490 config += wxT(",");
491 config += wxT("=") + str;
492 str = wxEmptyString;
493 break;
494 }
495 }
496 default:
497 {
498 str.Append(*cp);
499 break;
500 }
501 }
502 cp++;
503 }
504
505 if (!str.IsEmpty())
506 {
507 if (!config.IsEmpty())
508 config += wxT(",");
509 config += wxT("=") + str;
510 }
511
512 return config;
513 }
514
515
FillArray(wxArrayString & array,const wxString & list)516 void FillArray(wxArrayString &array, const wxString &list)
517 {
518 const wxChar *cp = list.c_str();
519
520 wxString str;
521 bool quote = false;
522
523 while (*cp)
524 {
525 switch (*cp)
526 {
527 case '\\':
528 {
529 if (cp[1] == '"')
530 {
531 cp++;
532 str.Append(*cp);
533 }
534 break;
535 }
536 case '"':
537 case '\'':
538 {
539 quote = !quote;
540 break;
541 }
542 case ',':
543 {
544 if (!quote)
545 {
546 array.Add(str);
547 str = wxEmptyString;
548 break;
549 }
550 }
551 default:
552 {
553 str.Append(*cp);
554 break;
555 }
556 }
557 cp++;
558 }
559 if (!str.IsEmpty())
560 array.Add(str);
561 }
562
563
queryTokenizer(const wxString & str,const wxChar delim)564 queryTokenizer::queryTokenizer(const wxString &str, const wxChar delim)
565 : wxStringTokenizer()
566 {
567 if (delim == (wxChar)' ')
568 SetString(str, wxT(" \n\r\t"), wxTOKEN_RET_EMPTY_ALL);
569 else
570 SetString(str, delim, wxTOKEN_RET_EMPTY_ALL);
571 delimiter = delim;
572 }
573
574
AppendIfFilled(wxString & str,const wxString & delimiter,const wxString & what)575 void AppendIfFilled(wxString &str, const wxString &delimiter, const wxString &what)
576 {
577 if (!what.IsNull())
578 str += delimiter + what;
579 }
580
581
GetNextToken()582 wxString queryTokenizer::GetNextToken()
583 {
584 // we need to override wxStringTokenizer, because we have to handle quotes
585 wxString str;
586
587 bool foundQuote = false;
588 do
589 {
590 wxString s = wxStringTokenizer::GetNextToken();
591 str.Append(s);
592 int quotePos;
593 do
594 {
595 quotePos = s.Find('"');
596 if (quotePos >= 0)
597 {
598 foundQuote = !foundQuote;
599 s = s.Mid(quotePos + 1);
600 }
601 }
602 while (quotePos >= 0);
603
604 if (foundQuote)
605 str.Append(delimiter);
606 }
607 while (foundQuote & HasMoreTokens());
608
609 return str;
610 }
611
612
FileRead(const wxString & filename,int format)613 wxString FileRead(const wxString &filename, int format)
614 {
615 wxString str;
616
617 wxFontEncoding encoding;
618 if (format > 0)
619 encoding = wxFONTENCODING_UTF8;
620 else
621 encoding = wxFONTENCODING_DEFAULT;
622
623 wxUtfFile file(filename, wxFile::read, encoding);
624
625 if (file.IsOpened())
626 file.Read(str);
627
628 return wxTextBuffer::Translate(str, wxTextFileType_Unix); // we want only \n
629 }
630
631
FileWrite(const wxString & filename,const wxString & data,int format)632 bool FileWrite(const wxString &filename, const wxString &data, int format)
633 {
634 wxFontEncoding encoding = wxFONTENCODING_DEFAULT;
635 wxUtfFile file;
636
637 if (format < 0)
638 {
639 if (wxFile::Access(filename, wxFile::read))
640 {
641 file.Open(filename);
642 encoding = file.GetEncoding();
643 file.Close();
644 }
645 if (encoding == wxFONTENCODING_DEFAULT)
646 encoding = settings->GetUnicodeFile() ? wxFONTENCODING_UTF8 : wxFONTENCODING_SYSTEM;
647 }
648 else
649 encoding = format ? wxFONTENCODING_UTF8 : wxFONTENCODING_SYSTEM;
650
651
652 file.Open(filename, wxFile::write, wxS_DEFAULT, encoding);
653
654 if (file.IsOpened())
655 return file.Write(wxTextBuffer::Translate(data));
656
657 return false;
658 }
659
660
CleanHelpPath(const wxString & path)661 wxString CleanHelpPath(const wxString &path)
662 {
663 wxString thePath = path;
664
665 if (thePath.IsEmpty())
666 return wxEmptyString;
667
668 // If the filename ends in .chm, .hhp, .pdf or .zip, it's
669 // a file based help system and we can assumes it's
670 // correct.
671 if (thePath.Lower().EndsWith(wxT(".hhp")) ||
672 #if defined (__WXMSW__) || wxUSE_LIBMSPACK
673 thePath.Lower().EndsWith(wxT(".chm")) ||
674 #endif
675 thePath.Lower().EndsWith(wxT(".pdf")) ||
676 thePath.Lower().EndsWith(wxT(".zip")))
677 return thePath;
678
679 // In all other cases we must have a directory
680 wxString sep;
681
682 // Figure out the appropriate seperator
683 if (thePath.Lower().Contains(wxT("://")))
684 sep = wxT("/");
685 else
686 sep = wxFileName::GetPathSeparator();
687
688 // First, correct the path separators
689 thePath.Replace(wxT("/"), sep);
690 thePath.Replace(wxT("\\"), sep);
691
692 // Now, append the seperator if it's not there
693 if (!thePath.EndsWith(sep))
694 thePath += sep;
695
696 return thePath;
697 }
698
HelpPathValid(const wxString & path)699 bool HelpPathValid(const wxString &path)
700 {
701 if (path.IsEmpty())
702 return true;
703
704 // If the filename contains any of the sensible URL schemes
705 // we just assume it's correct if the end also looks right.
706 if ((path.Lower().StartsWith(wxT("http://")) ||
707 path.Lower().StartsWith(wxT("https://")) ||
708 path.Lower().StartsWith(wxT("file://")) ||
709 path.Lower().StartsWith(wxT("ftp://"))) &&
710 (path.Lower().EndsWith(wxT(".hhp")) ||
711 #if defined (__WXMSW__) || wxUSE_LIBMSPACK
712 path.Lower().EndsWith(wxT(".chm")) ||
713 #endif
714 path.Lower().EndsWith(wxT(".pdf")) ||
715 path.Lower().EndsWith(wxT(".zip")) ||
716 path.Lower().EndsWith(wxT("/"))))
717 return true;
718
719 // Otherwise, we're looking for a file (or directory) that
720 // actually exists.
721 wxString sep = wxFileName::GetPathSeparator();
722 if (path.Lower().EndsWith(wxT(".hhp")) ||
723 #if defined (__WXMSW__) || wxUSE_LIBMSPACK
724 path.Lower().EndsWith(wxT(".chm")) ||
725 #endif
726 path.Lower().EndsWith(wxT(".pdf")) ||
727 path.Lower().EndsWith(wxT(".zip")))
728 return wxFile::Exists(path);
729 else if (path.Lower().EndsWith(sep))
730 return wxDir::Exists(path);
731 else
732 return false;
733 }
734
DisplayHelp(const wxString & helpTopic,const HelpType helpType)735 void DisplayHelp(const wxString &helpTopic, const HelpType helpType)
736 {
737 static wxHelpControllerBase *pgHelpCtl = 0;
738 static wxHelpControllerBase *edbHelpCtl = 0;
739 static wxHelpControllerBase *greenplumHelpCtl = 0;
740 static wxHelpControllerBase *slonyHelpCtl = 0;
741 static wxString pgInitPath = wxEmptyString;
742 static wxString edbInitPath = wxEmptyString;
743 static wxString gpInitPath = wxEmptyString;
744 static wxString slonyInitPath = wxEmptyString;
745
746 switch (helpType)
747 {
748 case HELP_PGADMIN:
749 DisplayPgAdminHelp(helpTopic);
750 break;
751
752 case HELP_POSTGRESQL:
753 DisplayExternalHelp(helpTopic, settings->GetPgHelpPath(), pgHelpCtl, (pgInitPath != settings->GetPgHelpPath() ? true : false));
754 pgInitPath = settings->GetPgHelpPath();
755 break;
756
757 case HELP_ENTERPRISEDB:
758 DisplayExternalHelp(helpTopic, settings->GetEdbHelpPath(), edbHelpCtl, (edbInitPath != settings->GetEdbHelpPath() ? true : false));
759 edbInitPath = settings->GetEdbHelpPath();
760 break;
761
762 case HELP_GREENPLUM:
763 {
764 // the old help path (stored in the settings) is no longer working
765 static wxString gpHelpPath = settings->GetGpHelpPath();
766
767 // Note: never end the URL on "index.html"
768 // InitHelp() does obscure magic with this ending
769 if (gpHelpPath.CmpNoCase(wxT("http://docs.gopivotal.com/gpdb/")) == 0)
770 {
771 gpHelpPath = wxT("http://gpdb.docs.pivotal.io/");
772 // Replace the path to the old domain with the link to
773 // the new documentation path
774 // The old link is working for now, but there is no guarantee
775 // that it will stay this way
776 // Also the new link automatically redirects to the latest version
777 settings->SetGpHelpPath(gpHelpPath);
778 }
779
780 if (gpHelpPath.CmpNoCase(wxT("http://www.greenplum.com/docs/3300/")) == 0)
781 {
782 gpHelpPath = wxT("http://gpdb.docs.pivotal.io/");
783 // this is the old link, update the link to the new documentation link
784 // problem: this saves the link into the configuration file
785 settings->SetGpHelpPath(gpHelpPath);
786 }
787 DisplayExternalHelp(helpTopic, settings->GetGpHelpPath(), greenplumHelpCtl, (gpInitPath != settings->GetGpHelpPath() ? true : false));
788 gpInitPath = settings->GetGpHelpPath();
789 }
790 break;
791
792 case HELP_SLONY:
793 DisplayExternalHelp(helpTopic, settings->GetSlonyHelpPath(), slonyHelpCtl, (slonyInitPath != settings->GetSlonyHelpPath() ? true : false));
794 slonyInitPath = settings->GetSlonyHelpPath();
795 break;
796
797 default:
798 DisplayPgAdminHelp(helpTopic);
799 break;
800 }
801 }
802
DisplayPgAdminHelp(const wxString & helpTopic)803 void DisplayPgAdminHelp(const wxString &helpTopic)
804 {
805 static wxHelpControllerBase *helpCtl = 0;
806 static bool firstCall = true;
807
808 // Startup the main help system
809 if (firstCall)
810 {
811 firstCall = false;
812 wxString helpdir = docPath + wxT("/") + settings->GetCanonicalLanguageName();
813
814 if (!wxFile::Exists(helpdir + wxT("/pgadmin3.hhp")) &&
815 #if defined(__WXMSW__) || wxUSE_LIBMSPACK
816 !wxFile::Exists(helpdir + wxT("/pgadmin3.chm")) &&
817 #endif
818 !wxFile::Exists(helpdir + wxT("/pgadmin3.zip")))
819 helpdir = docPath + wxT("/en_US");
820
821 #ifdef __WXMSW__
822 #ifndef __WXDEBUG__
823 if (wxFile::Exists(helpdir + wxT("/pgadmin3.chm")))
824 {
825 helpCtl = new wxCHMHelpController();
826 helpCtl->Initialize(helpdir + wxT("/pgadmin3"));
827 }
828 else
829 #endif
830 #endif
831 #if wxUSE_LIBMSPACK
832 if (wxFile::Exists(helpdir + wxT("/pgadmin3.chm")) ||
833 wxFile::Exists(helpdir + wxT("/pgadmin3.hhp")) || wxFile::Exists(helpdir + wxT("/pgadmin3.zip")))
834 #else
835 if (wxFile::Exists(helpdir + wxT("/pgadmin3.hhp")) || wxFile::Exists(helpdir + wxT("/pgadmin3.zip")))
836 #endif
837 {
838 helpCtl = new wxHtmlHelpController();
839 helpCtl->Initialize(helpdir + wxT("/pgadmin3"));
840 }
841 }
842
843 wxString page;
844 int hashPos = helpTopic.Find('#');
845 if (hashPos < 0)
846 page = helpTopic + wxT(".html");
847 else
848 page = helpTopic.Left(hashPos) + wxT(".html") + helpTopic.Mid(hashPos);
849
850 if (helpCtl)
851 {
852 if (helpTopic == wxT("index.html"))
853 helpCtl->DisplayContents();
854 else
855 helpCtl->DisplaySection(page);
856 }
857 else
858 {
859 wxLaunchDefaultBrowser(page);
860 }
861 }
862
DisplayExternalHelp(const wxString & helpTopic,const wxString & docPath,wxHelpControllerBase * helpCtl,const bool init)863 void DisplayExternalHelp(const wxString &helpTopic, const wxString &docPath, wxHelpControllerBase *helpCtl, const bool init)
864 {
865 // Build the page name
866 wxString page;
867 int hashPos = helpTopic.Find('#');
868 if (hashPos < 0)
869 page = helpTopic + wxT(".html");
870 else
871 page = helpTopic.Left(hashPos) + wxT(".html") + helpTopic.Mid(hashPos);
872
873 // If the docPath ends in .pdf, then open the file in the browser. No
874 // bookmarks though :-(
875 if (docPath.Lower().EndsWith(wxT(".pdf")))
876 {
877 wxLaunchDefaultBrowser(docPath);
878 return;
879 }
880
881 // If the docPath doesn't end in .chm, .zip or .hhp, then we must be using
882 // plain HTML files, so just fire off the browser and be done with it.
883 if (!docPath.Lower().EndsWith(wxT(".hhp")) &&
884 #if defined (__WXMSW__) || wxUSE_LIBMSPACK
885 !docPath.Lower().EndsWith(wxT(".chm")) &&
886 #endif
887 !docPath.Lower().EndsWith(wxT(".zip")))
888 {
889 wxLaunchDefaultBrowser(docPath + page);
890 return;
891 }
892
893 // We must be using HTML Help, so init the appropriate help controller
894 // Note the path that we init for - if it changes, we need to init a
895 // new controller in case it's no longer the same type
896 if (init || !helpCtl)
897 {
898 // Get shot of the old help controller if there is one.
899 if (helpCtl)
900 delete helpCtl;
901
902 #ifdef __WXMSW__
903 // For Windows builds we us the MS HTML Help viewer for .chm files
904 if (docPath.Lower().EndsWith(wxT(".chm")) && wxFile::Exists(docPath))
905 {
906 helpCtl = new wxCHMHelpController();
907 helpCtl->Initialize(docPath);
908 }
909 else
910 #endif
911 #if wxUSE_LIBMSPACK
912 // If we can use a .chm file...
913 if ((docPath.Lower().EndsWith(wxT(".chm")) && wxFile::Exists(docPath)) ||
914 (docPath.Lower().EndsWith(wxT(".hhp")) && wxFile::Exists(docPath)) ||
915 (docPath.Lower().EndsWith(wxT(".zip")) && wxFile::Exists(docPath)))
916 #else
917 // Otherwise...
918 if ((docPath.Lower().EndsWith(wxT(".hhp")) && wxFile::Exists(docPath)) ||
919 (docPath.Lower().EndsWith(wxT(".zip")) && wxFile::Exists(docPath)))
920 #endif
921 {
922 helpCtl = new wxHtmlHelpController();
923 helpCtl->Initialize(docPath);
924 }
925 }
926
927 // Display the page using the help controller
928 // If it's foobar'ed, use the browser.
929 if (helpCtl)
930 {
931 if (helpTopic == wxT("index.html"))
932 helpCtl->DisplayContents();
933 else
934 helpCtl->DisplaySection(page);
935 }
936 else
937 {
938 wxLogError(_("The help source (\"%s\") could not be opened. Please check the help configuration options."), docPath.c_str());
939 }
940 }
941
GetHtmlEntity(const wxChar ch)942 wxString GetHtmlEntity(const wxChar ch)
943 {
944 // REWRITE THIS - IT'S (STILL) BLOODY INEFFICIENT!!
945
946 // Quick bailout
947 if ((ch >= 'a' && ch <= 'z') ||
948 (ch >= 'A' && ch <= 'Z') ||
949 (ch >= '0' && ch <= '9'))
950 return wxString(ch);
951
952 unsigned short ents[] =
953 {
954 34,
955 39,
956 38,
957 60,
958 62,
959 160,
960 161,
961 164,
962 162,
963 163,
964 165,
965 166,
966 167,
967 168,
968 169,
969 170,
970 171,
971 172,
972 173,
973 174,
974 8482,
975 175,
976 176,
977 177,
978 178,
979 179,
980 180,
981 181,
982 182,
983 183,
984 184,
985 185,
986 186,
987 187,
988 188,
989 189,
990 190,
991 191,
992 215,
993 247,
994 192,
995 193,
996 194,
997 195,
998 196,
999 197,
1000 198,
1001 199,
1002 200,
1003 201,
1004 202,
1005 203,
1006 204,
1007 205,
1008 206,
1009 207,
1010 208,
1011 209,
1012 210,
1013 211,
1014 212,
1015 213,
1016 214,
1017 216,
1018 217,
1019 218,
1020 219,
1021 220,
1022 221,
1023 222,
1024 223,
1025 224,
1026 225,
1027 226,
1028 227,
1029 228,
1030 229,
1031 230,
1032 231,
1033 232,
1034 233,
1035 234,
1036 235,
1037 236,
1038 237,
1039 238,
1040 239,
1041 240,
1042 241,
1043 242,
1044 243,
1045 244,
1046 245,
1047 246,
1048 248,
1049 249,
1050 250,
1051 251,
1052 252,
1053 253,
1054 254,
1055 255,
1056 338,
1057 339,
1058 352,
1059 353,
1060 376,
1061 710,
1062 732,
1063 8194,
1064 8195,
1065 8201,
1066 8204,
1067 8205,
1068 8206,
1069 8207,
1070 8211,
1071 8212,
1072 8216,
1073 8217,
1074 8218,
1075 8220,
1076 8221,
1077 8222,
1078 8224,
1079 8225,
1080 8230,
1081 8240,
1082 8249,
1083 8250,
1084 8364,
1085 0
1086 };
1087
1088 int elem = 0;
1089
1090 while (ents[elem] != 0)
1091 {
1092 if (ch == ents[elem])
1093 {
1094 wxString ret = wxT("&#");
1095 ret += NumToStr((long)ents[elem]);
1096 ret += wxT(";");
1097 return ret;
1098 }
1099 elem++;
1100 }
1101
1102 return wxString(ch);
1103 }
1104
HtmlEntities(const wxString & str)1105 wxString HtmlEntities(const wxString &str)
1106 {
1107 wxString ret;
1108
1109 for (unsigned int x = 0; x < str.Length(); x++)
1110 {
1111 if (str[x] != 13)
1112 ret += GetHtmlEntity(str[x]);
1113 }
1114
1115 return ret;
1116 }
1117
1118
1119 #ifndef WIN32
ExecProcess(const wxString & cmd)1120 wxString ExecProcess(const wxString &cmd)
1121 {
1122 wxString res;
1123 FILE *f = popen(cmd.ToAscii(), "r");
1124
1125 if (f)
1126 {
1127 char buffer[1024];
1128 int cnt;
1129 while ((cnt = fread(buffer, 1, 1024, f)) > 0)
1130 {
1131 res += wxString::FromAscii(buffer);
1132 }
1133 pclose(f);
1134 }
1135 return res;
1136 }
1137
ExecProcess(const wxString & command,wxArrayString & result)1138 int ExecProcess(const wxString &command, wxArrayString &result)
1139 {
1140 FILE *fp_command;
1141 char buf[4098];
1142
1143 fp_command = popen(command.mb_str(wxConvUTF8), "r");
1144
1145 if (!fp_command)
1146 return -1;
1147
1148 while(!feof(fp_command))
1149 {
1150 if (fgets(buf, 4096, fp_command) != NULL)
1151 result.Add(wxString::FromAscii(buf));
1152 }
1153
1154 return pclose(fp_command);
1155 }
1156 #endif
1157
1158 #ifdef WIN32
1159 #if (_MSC_VER < 1300)
1160 /* _ftol2 is more than VC7. */
1161 extern "C" long _ftol( double );
_ftol2(double dblSource)1162 extern "C" long _ftol2( double dblSource )
1163 {
1164 return _ftol( dblSource );
1165 }
1166 #endif
1167 #endif
1168
firstLineOnly(const wxString & str)1169 wxString firstLineOnly(const wxString &str)
1170 {
1171 wxString ip, tmp;
1172 ip = str;
1173 ip = ip.Trim(true).Trim(false);
1174
1175 if (ip.Contains(wxT("\r\n")) && (ip.First(wxT("\r")) < ip.First(wxT("\n"))))
1176 {
1177 tmp = ip.BeforeFirst('\r');
1178 if (ip.BeforeFirst('\r').Length() != ip.Length())
1179 tmp += wxT("...");
1180 }
1181 else
1182 {
1183 tmp = ip.BeforeFirst('\n');
1184 if (ip.BeforeFirst('\n').Length() != ip.Length())
1185 tmp += wxT("...");
1186 }
1187
1188 return tmp;
1189 }
1190
pgAppMinimumVersion(const wxString & cmd,const int majorVer,const int minorVer)1191 bool pgAppMinimumVersion(const wxString &cmd, const int majorVer, const int minorVer)
1192 {
1193 wxArrayString output;
1194 bool isEnterpriseDB = false;
1195
1196 #ifdef __WXMSW__
1197 if (wxExecute(cmd + wxT(" --version"), output, 0) != 0)
1198 #else
1199 if (ExecProcess(cmd + wxT(" --version"), output) != 0)
1200 #endif
1201 {
1202 wxLogError(_("Failed to execute: %s --version"), cmd.c_str());
1203 return false;
1204 }
1205
1206 // Is this an EDB utility?
1207 if (output[0].Contains(wxT("EnterpriseDB")))
1208 isEnterpriseDB = true;
1209
1210 wxString version = output[0].AfterLast(' ');
1211 long actualMajor = 0, actualMinor = 0;
1212
1213 wxString tmp = wxT("");
1214 int x = 0;
1215 while(version[x] == '0' || version[x] == '1' || version[x] == '2' || version[x] == '3' || version[x] == '4' ||
1216 version[x] == '5' || version[x] == '6' || version[x] == '7' || version[x] == '8' || version[x] == '9')
1217 {
1218 tmp += version[x];
1219 x++;
1220 }
1221 tmp.ToLong(&actualMajor);
1222 x++;
1223
1224 tmp = wxT("");
1225 while(version[x] == '0' || version[x] == '1' || version[x] == '2' || version[x] == '3' || version[x] == '4' ||
1226 version[x] == '5' || version[x] == '6' || version[x] == '7' || version[x] == '8' || version[x] == '9')
1227 {
1228 tmp += version[x];
1229 x++;
1230 }
1231
1232 tmp.ToLong(&actualMinor);
1233
1234 // EnterpriseDB's 8.3R1 utilties are based on PG8.2, so correct the version number here.
1235 // This will need more work when 8.3R2 is released :-(
1236 if (isEnterpriseDB && actualMajor == 8 && actualMinor == 3)
1237 actualMinor = 2;
1238
1239 if (actualMajor > majorVer)
1240 return true;
1241
1242 if (actualMajor == majorVer && actualMinor >= minorVer)
1243 return true;
1244
1245 return false;
1246 }
1247
isPgApp(const wxString & app)1248 bool isPgApp(const wxString &app)
1249 {
1250 if (!wxFile::Exists(app))
1251 return false;
1252
1253 wxArrayString output;
1254
1255 #ifdef __WXMSW__
1256 if (wxExecute(app + wxT(" --version"), output, 0) != 0)
1257 #else
1258 if (ExecProcess(app + wxT(" --version"), output) != 0)
1259 #endif
1260 {
1261 wxLogError(_("Failed to execute: %s --version"), app.c_str());
1262 return false;
1263 }
1264
1265 if (output[0].Contains(wxT("PostgreSQL")))
1266 return true;
1267
1268 return false;
1269 }
1270
isEdbApp(const wxString & app)1271 bool isEdbApp(const wxString &app)
1272 {
1273 if (!wxFile::Exists(app))
1274 return false;
1275
1276 wxArrayString output;
1277
1278 #ifdef __WXMSW__
1279 if (wxExecute(app + wxT(" --version"), output, 0) != 0)
1280 #else
1281 if (ExecProcess(app + wxT(" --version"), output) != 0)
1282 #endif
1283 {
1284 wxLogError(_("Failed to execute: %s --version"), app.c_str());
1285 return false;
1286 }
1287
1288 if (output[0].Contains(wxT("EnterpriseDB")))
1289 return true;
1290
1291 return false;
1292 }
1293
isGpApp(const wxString & app)1294 bool isGpApp(const wxString &app)
1295 {
1296 if (!wxFile::Exists(app))
1297 return false;
1298
1299 wxArrayString output;
1300
1301 #ifdef __WXMSW__
1302 if (wxExecute(app + wxT(" --version"), output, 0) != 0)
1303 #else
1304 if (ExecProcess(app + wxT(" --version"), output) != 0)
1305 #endif
1306 {
1307 wxLogError(_("Failed to execute: %s --version"), app.c_str());
1308 return false;
1309 }
1310
1311 if (output[0].Contains(wxT("8.2"))) // Ugly... No way to tell Greenplum app from PostgreSQL 8.2 app
1312 return true;
1313
1314 return false;
1315 }
1316
sanitizePath(const wxString & path)1317 wxString sanitizePath(const wxString &path)
1318 {
1319 if (path.Length())
1320 {
1321 wxFileName fn = path;
1322 fn.Normalize();
1323 return fn.GetLongPath();
1324 }
1325
1326 return wxEmptyString;
1327 }
1328
1329 /**
1330 * FUNCTION: commandLineCleanOption
1331 * INPUTS:
1332 * option - input string needs to be reformatted
1333 * schemaObject - Is this an object related to schema?
1334 * PURPOSE:
1335 * - Fixup a (double-quoted) string for use on the command line
1336 */
commandLineCleanOption(const wxString & option,bool schemaObject)1337 wxString commandLineCleanOption(const wxString &option, bool schemaObject)
1338 {
1339 wxString tmp = option;
1340
1341 if (schemaObject)
1342 {
1343 // Replace double-quote with slash & double-quote
1344 tmp.Replace(wxT("\""), wxT("\\\""));
1345 }
1346 else
1347 {
1348 // If required, clean the string to know the real object name
1349 if (option.StartsWith(wxT("\"")) && option.EndsWith(wxT("\"")))
1350 tmp = option.AfterFirst((wxChar)'"').BeforeLast((wxChar)'"');
1351
1352 // Replace single splash to double-splash
1353 tmp.Replace(wxT("\\"), wxT("\\\\"));
1354
1355 // Replace double-quote with slash & double-quote
1356 tmp.Replace(wxT("\""), wxT("\\\""));
1357
1358 // Replace double (slash & double-quote) combination to single (slash & double-quote) combination
1359 tmp.Replace(wxT("\\\"\\\""), wxT("\\\""));
1360
1361 // Add the double quotes
1362 tmp = wxT("\"") + tmp + wxT("\"");
1363 }
1364
1365 return tmp;
1366 }
1367
1368 #endif // PGSCLI
1369
1370 // Get an array from a comma separated list
getArrayFromCommaSeparatedList(const wxString & str,wxArrayString & res)1371 bool getArrayFromCommaSeparatedList(const wxString &str, wxArrayString &res)
1372 {
1373 size_t len = str.Len(), index = 0, nBracketLevel = 0, startArray = 0;
1374 bool inSingleQuote = false, inDoubleQuote = false;
1375
1376 if (len == 0)
1377 return true;
1378
1379 for(; index < len; index++)
1380 {
1381 wxChar curr = str.GetChar(index);
1382 if (!inDoubleQuote && curr == (wxChar)'\'')
1383 inSingleQuote = !inSingleQuote;
1384 else if (!inSingleQuote && curr == (wxChar)'"')
1385 inDoubleQuote = !inDoubleQuote;
1386 else if (!inDoubleQuote && !inSingleQuote && curr == (wxChar)'(')
1387 nBracketLevel++;
1388 else if (!inDoubleQuote && !inSingleQuote && curr == (wxChar)')')
1389 nBracketLevel--;
1390 else if (!inDoubleQuote && !inSingleQuote && nBracketLevel == 0 && curr == (wxChar)',')
1391 {
1392 if (index != startArray)
1393 res.Add(str.SubString(startArray, index - 1).Trim(true).Trim(false));
1394 else
1395 res.Add(wxEmptyString);
1396 startArray = index + 1;
1397 }
1398 }
1399 if (inDoubleQuote || inSingleQuote || nBracketLevel != 0)
1400 return false;
1401
1402 // Add last value to array
1403 res.Add(str.SubString(startArray, index).Trim(true).Trim(false));
1404
1405 return true;
1406 }
1407
1408