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