tinyfiledialogs.c
Go to the documentation of this file.
1 /*_________
2  / \ tinyfiledialogs.c v3.4.1 [Oct 29, 2019] zlib licence
3  |tiny file| Unique code file created [November 9, 2014]
4  | dialogs | Copyright (c) 2014 - 2018 Guillaume Vareille http://ysengrin.com
5  \____ ___/ http://tinyfiledialogs.sourceforge.net
6  \| git clone http://git.code.sf.net/p/tinyfiledialogs/code tinyfd
7  ____________________________________________
8  | |
9  | email: tinyfiledialogs at ysengrin.com |
10  |____________________________________________|
11  ___________________________________________________________________
12  | |
13  | the windows only wchar_t UTF-16 prototypes are in the header file |
14  |___________________________________________________________________|
15 
16 Please upvote my stackoverflow answer https://stackoverflow.com/a/47651444
17 
18 tiny file dialogs (cross-platform C C++)
19 InputBox PasswordBox MessageBox ColorPicker
20 OpenFileDialog SaveFileDialog SelectFolderDialog
21 Native dialog library for WINDOWS MAC OSX GTK+ QT CONSOLE & more
22 SSH supported via automatic switch to console mode or X11 forwarding
23 
24 one C file + a header (add them to your C or C++ project) with 8 functions:
25 - beep
26 - notify popup (tray)
27 - message & question
28 - input & password
29 - save file
30 - open file(s)
31 - select folder
32 - color picker
33 
34 Complements OpenGL Vulkan GLFW GLUT GLUI VTK SFML TGUI
35 SDL Ogre Unity3d ION OpenCV CEGUI MathGL GLM CPW GLOW
36 Open3D IMGUI MyGUI GLT NGL STB & GUI less programs
37 
38 NO INIT
39 NO MAIN LOOP
40 NO LINKING
41 NO INCLUDE
42 
43 The dialogs can be forced into console mode
44 
45 Windows (XP to 10) ASCII MBCS UTF-8 UTF-16
46 - native code & vbs create the graphic dialogs
47 - enhanced console mode can use dialog.exe from
48 http://andrear.altervista.org/home/cdialog.php
49 - basic console input
50 
51 Unix (command line calls) ASCII UTF-8
52 - applescript, kdialog, zenity
53 - python (2 or 3) + tkinter + python-dbus (optional)
54 - dialog (opens a console if needed)
55 - basic console input
56 The same executable can run across desktops & distributions
57 
58 C89 & C++98 compliant: tested with C & C++ compilers
59 VisualStudio MinGW-gcc GCC Clang TinyCC OpenWatcom-v2 BorlandC SunCC ZapCC
60 on Windows Mac Linux Bsd Solaris Minix Raspbian
61 using Gnome Kde Enlightenment Mate Cinnamon Budgie Unity Lxde Lxqt Xfce
62 WindowMaker IceWm Cde Jds OpenBox Awesome Jwm Xdm
63 
64 Bindings for LUA and C# dll, Haskell
65 Included in LWJGL(java), Rust, Allegrobasic
66 
67 Thanks for contributions, bug corrections & thorough testing to:
68 - Don Heyse http://ldglite.sf.net for bug corrections & thorough testing!
69 - Paul Rouget
70 
71 - License -
72 
73 This software is provided 'as-is', without any express or implied
74 warranty. In no event will the authors be held liable for any damages
75 arising from the use of this software.
76 
77 Permission is granted to anyone to use this software for any purpose,
78 including commercial applications, and to alter it and redistribute it
79 freely, subject to the following restrictions:
80 
81 1. The origin of this software must not be misrepresented; you must not
82 claim that you wrote the original software. If you use this software
83 in a product, an acknowledgment in the product documentation would be
84 appreciated but is not required.
85 2. Altered source versions must be plainly marked as such, and must not be
86 misrepresented as being the original software.
87 3. This notice may not be removed or altered from any source distribution.
88 */
89 
90 #ifndef __sun
91 #define _POSIX_C_SOURCE 2 /* to accept POSIX 2 in old ANSI C standards */
92 #endif
93 
94 #include <stdio.h>
95 #include <stdlib.h>
96 #include <string.h>
97 #include <ctype.h>
98 #include <sys/stat.h>
99 
100 #include "tinyfiledialogs.h"
101 /* #define TINYFD_NOLIB */
102 
103 #ifdef _WIN32
104  #ifdef __BORLANDC__
105  #define _getch getch
106  #endif
107  #ifndef _WIN32_WINNT
108  #define _WIN32_WINNT 0x0500
109  #endif
110  #ifndef TINYFD_NOLIB
111  #include <windows.h>
112  /*#define TINYFD_NOSELECTFOLDERWIN*/
113  #ifndef TINYFD_NOSELECTFOLDERWIN
114  #include <shlobj.h>
115  #endif /*TINYFD_NOSELECTFOLDERWIN*/
116  #endif
117  #include <conio.h>
118  #include <commdlg.h>
119  #define TINYFD_NOCCSUNICODE
120  #define SLASH "\\"
121  int tinyfd_winUtf8 = 0 ; /* on windows string char can be 0:MBCS or 1:UTF-8 */
122 #else
123  #include <limits.h>
124  #include <unistd.h>
125  #include <dirent.h> /* on old systems try <sys/dir.h> instead */
126  #include <termios.h>
127  #include <sys/utsname.h>
128  #include <signal.h> /* on old systems try <sys/signal.h> instead */
129  #define SLASH "/"
130 #endif /* _WIN32 */
131 
132 #define MAX_PATH_OR_CMD 1024 /* _MAX_PATH or MAX_PATH */
133 #define MAX_MULTIPLE_FILES 32
134 
135 char const tinyfd_version [8] = "3.4.1";
136 
137 int tinyfd_verbose = 0 ; /* on unix: prints the command line calls */
138 int tinyfd_silent = 1 ; /* 1 (default) or 0 : on unix,
139  hide errors and warnings from called dialog*/
140 
141 #if defined(TINYFD_NOLIB) && defined(_WIN32)
142 int tinyfd_forceConsole = 1 ;
143 #else
144 int tinyfd_forceConsole = 0 ; /* 0 (default) or 1 */
145 #endif
146 /* for unix & windows: 0 (graphic mode) or 1 (console mode).
147 0: try to use a graphic solution, if it fails then it uses console mode.
148 1: forces all dialogs into console mode even when the X server is present,
149  if the package dialog (and a console is present) or dialog.exe is installed.
150  on windows it only make sense for console applications */
151 
152 char tinyfd_response[1024];
153 /* if you pass "tinyfd_query" as aTitle,
154 the functions will not display the dialogs
155 but and return 0 for console mode, 1 for graphic mode.
156 tinyfd_response is then filled with the retain solution.
157 possible values for tinyfd_response are (all lowercase)
158 for graphic mode:
159  windows_wchar windows
160  applescript kdialog zenity zenity3 matedialog qarma
161  python2-tkinter python3-tkinter python-dbus perl-dbus
162  gxmessage gmessage xmessage xdialog gdialog
163 for console mode:
164  dialog whiptail basicinput no_solution */
165 
166 #if defined(TINYFD_NOLIB) && defined(_WIN32)
167 static int gWarningDisplayed = 1 ;
168 #else
169 static int gWarningDisplayed = 0 ;
170 #endif
171 
172 static char const gTitle[]="missing software! (we will try basic console input)";
173 
174 #ifdef _WIN32
175 char const tinyfd_needs[] = "\
176  ___________\n\
177 / \\ \n\
178 | tiny file |\n\
179 | dialogs |\n\
180 \\_____ ____/\n\
181  \\|\
182 \ntiny file dialogs on Windows needs:\
183 \n a graphic display\
184 \nor dialog.exe (enhanced console mode)\
185 \nor a console for basic input";
186 #else
187 char const tinyfd_needs[] = "\
188  ___________\n\
189 / \\ \n\
190 | tiny file |\n\
191 | dialogs |\n\
192 \\_____ ____/\n\
193  \\|\
194 \ntiny file dialogs on UNIX needs:\
195 \n applescript\
196 \nor kdialog\
197 \nor zenity (or matedialog or qarma)\
198 \nor python (2 or 3)\
199 \n + tkinter + python-dbus (optional)\
200 \nor dialog (opens console if needed)\
201 \nor xterm + bash\
202 \n (opens console for basic input)\
203 \nor existing console for basic input";
204 #endif
205 
206 #ifdef _MSC_VER
207 #pragma warning(disable:4996) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */
208 #pragma warning(disable:4100) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */
209 #pragma warning(disable:4706) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */
210 #endif
211 
213  char * const aoDestination, /* make sure it is allocated, use _MAX_PATH */
214  char const * const aSource) /* aoDestination and aSource can be the same */
215 {
216  char const * lTmp ;
217  if ( aSource )
218  {
219  lTmp = strrchr(aSource, '/');
220  if (!lTmp)
221  {
222  lTmp = strrchr(aSource, '\\');
223  }
224  if (lTmp)
225  {
226  strncpy(aoDestination, aSource, lTmp - aSource );
227  aoDestination[lTmp - aSource] = '\0';
228  }
229  else
230  {
231  * aoDestination = '\0';
232  }
233  }
234  else
235  {
236  * aoDestination = '\0';
237  }
238  return aoDestination;
239 }
240 
241 
242 static char * getLastName(
243  char * const aoDestination, /* make sure it is allocated */
244  char const * const aSource)
245 {
246  /* copy the last name after '/' or '\' */
247  char const * lTmp ;
248  if ( aSource )
249  {
250  lTmp = strrchr(aSource, '/');
251  if (!lTmp)
252  {
253  lTmp = strrchr(aSource, '\\');
254  }
255  if (lTmp)
256  {
257  strcpy(aoDestination, lTmp + 1);
258  }
259  else
260  {
261  strcpy(aoDestination, aSource);
262  }
263  }
264  else
265  {
266  * aoDestination = '\0';
267  }
268  return aoDestination;
269 }
270 
271 
272 static void ensureFinalSlash( char * const aioString )
273 {
274  if ( aioString && strlen( aioString ) )
275  {
276  char * lastcar = aioString + strlen( aioString ) - 1 ;
277  if ( strncmp( lastcar , SLASH , 1 ) )
278  {
279  strcat( lastcar , SLASH ) ;
280  }
281  }
282 }
283 
284 
285 static void Hex2RGB( char const aHexRGB [8] ,
286  unsigned char aoResultRGB [3] )
287 {
288  char lColorChannel [8] ;
289  if ( aoResultRGB )
290  {
291  if ( aHexRGB )
292  {
293  strcpy(lColorChannel, aHexRGB ) ;
294  aoResultRGB[2] = (unsigned char)strtoul(lColorChannel+5,NULL,16);
295  lColorChannel[5] = '\0';
296  aoResultRGB[1] = (unsigned char)strtoul(lColorChannel+3,NULL,16);
297  lColorChannel[3] = '\0';
298  aoResultRGB[0] = (unsigned char)strtoul(lColorChannel+1,NULL,16);
299 /* printf("%d %d %d\n", aoResultRGB[0], aoResultRGB[1], aoResultRGB[2]); */
300  }
301  else
302  {
303  aoResultRGB[0]=0;
304  aoResultRGB[1]=0;
305  aoResultRGB[2]=0;
306  }
307  }
308 }
309 
310 static void RGB2Hex( unsigned char const aRGB [3] ,
311  char aoResultHexRGB [8] )
312 {
313  if ( aoResultHexRGB )
314  {
315  if ( aRGB )
316  {
317 #if defined(__GNUC__) && defined(_WIN32)
318  sprintf(aoResultHexRGB, "#%02hx%02hx%02hx",
319 #else
320  sprintf(aoResultHexRGB, "#%02hhx%02hhx%02hhx",
321 #endif
322  aRGB[0], aRGB[1], aRGB[2]);
323  /* printf("aoResultHexRGB %s\n", aoResultHexRGB); */
324  }
325  else
326  {
327  aoResultHexRGB[0]=0;
328  aoResultHexRGB[1]=0;
329  aoResultHexRGB[2]=0;
330  }
331  }
332 }
333 
334 
335 static void replaceSubStr( char const * const aSource ,
336  char const * const aOldSubStr ,
337  char const * const aNewSubStr ,
338  char * const aoDestination )
339 {
340  char const * pOccurence ;
341  char const * p ;
342  char const * lNewSubStr = "" ;
343  size_t lOldSubLen = strlen( aOldSubStr ) ;
344 
345  if ( ! aSource )
346  {
347  * aoDestination = '\0' ;
348  return ;
349  }
350  if ( ! aOldSubStr )
351  {
352  strcpy( aoDestination , aSource ) ;
353  return ;
354  }
355  if ( aNewSubStr )
356  {
357  lNewSubStr = aNewSubStr ;
358  }
359  p = aSource ;
360  * aoDestination = '\0' ;
361  while ( ( pOccurence = strstr( p , aOldSubStr ) ) != NULL )
362  {
363  strncat( aoDestination , p , pOccurence - p ) ;
364  strcat( aoDestination , lNewSubStr ) ;
365  p = pOccurence + lOldSubLen ;
366  }
367  strcat( aoDestination , p ) ;
368 }
369 
370 
371 static int filenameValid( char const * const aFileNameWithoutPath )
372 {
373  if ( ! aFileNameWithoutPath
374  || ! strlen(aFileNameWithoutPath)
375  || strpbrk(aFileNameWithoutPath , "\\/:*?\"<>|") )
376  {
377  return 0 ;
378  }
379  return 1 ;
380 }
381 
382 #ifndef _WIN32
383 
384 static int fileExists( char const * const aFilePathAndName )
385 {
386  FILE * lIn ;
387  if ( ! aFilePathAndName || ! strlen(aFilePathAndName) )
388  {
389  return 0 ;
390  }
391  lIn = fopen( aFilePathAndName , "r" ) ;
392  if ( ! lIn )
393  {
394  return 0 ;
395  }
396  fclose( lIn ) ;
397  return 1 ;
398 }
399 
400 #elif defined(TINYFD_NOLIB)
401 
402 static int fileExists( char const * const aFilePathAndName )
403 {
404  FILE * lIn ;
405  if ( ! aFilePathAndName || ! strlen(aFilePathAndName) )
406  {
407  return 0 ;
408  }
409 
410  if ( tinyfd_winUtf8 )
411  return 1; /* we cannot test */
412 
413  lIn = fopen( aFilePathAndName , "r" ) ;
414  if ( ! lIn )
415  {
416  return 0 ;
417  }
418  fclose( lIn ) ;
419  return 1 ;
420 }
421 
422 #endif
423 
424 
425 static void wipefile(char const * const aFilename)
426 {
427  int i;
428  struct stat st;
429  FILE * lIn;
430 
431  if (stat(aFilename, &st) == 0)
432  {
433  if ((lIn = fopen(aFilename, "w")))
434  {
435  for (i = 0; i < st.st_size; i++)
436  {
437  fputc('A', lIn);
438  }
439  }
440  fclose(lIn);
441  }
442 }
443 
444 
445 #ifdef _WIN32
446 
447 static int replaceChr( char * const aString ,
448  char const aOldChr ,
449  char const aNewChr )
450 {
451  char * p ;
452  int lRes = 0 ;
453 
454  if ( ! aString )
455  {
456  return 0 ;
457  }
458 
459  if ( aOldChr == aNewChr )
460  {
461  return 0 ;
462  }
463 
464  p = aString ;
465  while ( (p = strchr( p , aOldChr )) )
466  {
467  * p = aNewChr ;
468  p ++ ;
469  lRes = 1 ;
470  }
471  return lRes ;
472 }
473 
474 #ifdef TINYFD_NOLIB
475 
476 static int dirExists(char const * const aDirPath)
477 {
478  struct stat lInfo;
479 
480  if (!aDirPath || !strlen(aDirPath))
481  return 0;
482  if (stat(aDirPath, &lInfo) != 0)
483  return 0;
484  else if ( tinyfd_winUtf8 )
485  return 1; /* we cannot test */
486  else if (lInfo.st_mode & S_IFDIR)
487  return 1;
488  else
489  return 0;
490 }
491 
492 
493 void tinyfd_beep(void)
494 {
495  printf("\a");
496 }
497 
498 #else /* ndef TINYFD_NOLIB */
499 
500 void tinyfd_beep(void)
501 {
502  Beep(440,300);
503 }
504 
505 
506 static void wipefileW(wchar_t const * const aFilename)
507 {
508  int i;
509  struct _stat st;
510  FILE * lIn;
511 
512  if (_wstat(aFilename, &st) == 0)
513  {
514  if ((lIn = _wfopen(aFilename, L"w")))
515  {
516  for (i = 0; i < st.st_size; i++)
517  {
518  fputc('A', lIn);
519  }
520  }
521  fclose(lIn);
522  }
523 }
524 
525 
526 static wchar_t * getPathWithoutFinalSlashW(
527  wchar_t * const aoDestination, /* make sure it is allocated, use _MAX_PATH */
528  wchar_t const * const aSource) /* aoDestination and aSource can be the same */
529 {
530  wchar_t const * lTmp;
531  if (aSource)
532  {
533  lTmp = wcsrchr(aSource, L'/');
534  if (!lTmp)
535  {
536  lTmp = wcsrchr(aSource, L'\\');
537  }
538  if (lTmp)
539  {
540  wcsncpy(aoDestination, aSource, lTmp - aSource);
541  aoDestination[lTmp - aSource] = L'\0';
542  }
543  else
544  {
545  *aoDestination = L'\0';
546  }
547  }
548  else
549  {
550  *aoDestination = L'\0';
551  }
552  return aoDestination;
553 }
554 
555 
556 static wchar_t * getLastNameW(
557  wchar_t * const aoDestination, /* make sure it is allocated */
558  wchar_t const * const aSource)
559 {
560  /* copy the last name after '/' or '\' */
561  wchar_t const * lTmp;
562  if (aSource)
563  {
564  lTmp = wcsrchr(aSource, L'/');
565  if (!lTmp)
566  {
567  lTmp = wcsrchr(aSource, L'\\');
568  }
569  if (lTmp)
570  {
571  wcscpy(aoDestination, lTmp + 1);
572  }
573  else
574  {
575  wcscpy(aoDestination, aSource);
576  }
577  }
578  else
579  {
580  *aoDestination = L'\0';
581  }
582  return aoDestination;
583 }
584 
585 
586 static void Hex2RGBW(wchar_t const aHexRGB[8],
587  unsigned char aoResultRGB[3])
588 {
589  wchar_t lColorChannel[8];
590  if (aoResultRGB)
591  {
592  if (aHexRGB)
593  {
594  wcscpy(lColorChannel, aHexRGB);
595  aoResultRGB[2] = (unsigned char)wcstoul(lColorChannel + 5, NULL, 16);
596  lColorChannel[5] = '\0';
597  aoResultRGB[1] = (unsigned char)wcstoul(lColorChannel + 3, NULL, 16);
598  lColorChannel[3] = '\0';
599  aoResultRGB[0] = (unsigned char)wcstoul(lColorChannel + 1, NULL, 16);
600  /* printf("%d %d %d\n", aoResultRGB[0], aoResultRGB[1], aoResultRGB[2]); */
601  }
602  else
603  {
604  aoResultRGB[0] = 0;
605  aoResultRGB[1] = 0;
606  aoResultRGB[2] = 0;
607  }
608  }
609 }
610 
611 
612 static void RGB2HexW(
613  unsigned char const aRGB[3],
614  wchar_t aoResultHexRGB[8])
615 {
616  if (aoResultHexRGB)
617  {
618  if (aRGB)
619  {
620  /* wprintf(L"aoResultHexRGB %s\n", aoResultHexRGB); */
621  swprintf(aoResultHexRGB,
622 #if !defined(__BORLANDC__) && !defined(__TINYC__) && ( !defined(__GNUC__) || (__GNUC__) >= 5 )
623  8,
624 #endif
625  L"#%02hhx%02hhx%02hhx", aRGB[0], aRGB[1], aRGB[2]);
626  }
627  else
628  {
629  aoResultHexRGB[0] = 0;
630  aoResultHexRGB[1] = 0;
631  aoResultHexRGB[2] = 0;
632  }
633  }
634 }
635 
636 
637 #if !defined(WC_ERR_INVALID_CHARS)
638 /* undefined prior to Vista, so not yet in MINGW header file */
639 #define WC_ERR_INVALID_CHARS 0x00000080
640 #endif
641 
642 
643 static int sizeUtf16(char const * const aUtf8string)
644 {
645  return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
646  aUtf8string, -1, NULL, 0);
647 }
648 
649 
650 static int sizeUtf8(wchar_t const * const aUtf16string)
651 {
652  return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
653  aUtf16string, -1, NULL, 0, NULL, NULL);
654 }
655 
656 
657 static int sizeMbcs(wchar_t const * const aMbcsString)
658 {
659  int lRes = WideCharToMultiByte(CP_ACP, 0,
660  aMbcsString, -1, NULL, 0, NULL, NULL);
661  /* DWORD licic = GetLastError(); */
662  return lRes;
663 }
664 
665 
666 static wchar_t * utf8to16(char const * const aUtf8string)
667 {
668  wchar_t * lUtf16string ;
669  int lSize = sizeUtf16(aUtf8string);
670  lUtf16string = (wchar_t *) malloc( lSize * sizeof(wchar_t) );
671  lSize = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
672  aUtf8string, -1, lUtf16string, lSize);
673  if (lSize == 0)
674  {
675  free(lUtf16string);
676  return NULL;
677  }
678  return lUtf16string;
679 }
680 
681 
682 static wchar_t * mbcsTo16(char const * const aMbcsString)
683 {
684  wchar_t * lMbcsString;
685  int lSize = sizeUtf16(aMbcsString);
686  lMbcsString = (wchar_t *)malloc(lSize * sizeof(wchar_t));
687  lSize = MultiByteToWideChar(CP_ACP, 0,
688  aMbcsString, -1, lMbcsString, lSize);
689  if (lSize == 0)
690  {
691  free(lMbcsString);
692  return NULL;
693  }
694  return lMbcsString;
695 }
696 
697 
698 static char * utf16to8(wchar_t const * const aUtf16string)
699 {
700  char * lUtf8string ;
701  int lSize = sizeUtf8(aUtf16string);
702  lUtf8string = (char *) malloc( lSize );
703  lSize = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
704  aUtf16string, -1, lUtf8string, lSize, NULL, NULL);
705  if (lSize == 0)
706  {
707  free(lUtf8string);
708  return NULL;
709  }
710  return lUtf8string;
711 }
712 
713 
714 static char * utf16toMbcs(wchar_t const * const aUtf16string)
715 {
716  char * lMbcsString;
717  int lSize = sizeMbcs(aUtf16string);
718  lMbcsString = (char *)malloc(lSize);
719  lSize = WideCharToMultiByte(CP_ACP, 0,
720  aUtf16string, -1, lMbcsString, lSize, NULL, NULL);
721  if (lSize == 0)
722  {
723  free(lMbcsString);
724  return NULL;
725  }
726  return lMbcsString;
727 }
728 
729 
730 static int dirExists(char const * const aDirPath)
731 {
732  struct _stat lInfo;
733  wchar_t * lTmpWChar;
734  int lStatRet;
735  int lDirLen;
736 
737  if (!aDirPath)
738  return 0;
739  lDirLen = strlen(aDirPath);
740  if (!lDirLen)
741  return 1;
742  if ( (lDirLen == 2) && (aDirPath[1] == ':') )
743  return 1;
744 
745  if (tinyfd_winUtf8)
746  {
747  lTmpWChar = utf8to16(aDirPath);
748  lStatRet = _wstat(lTmpWChar, &lInfo);
749  free(lTmpWChar);
750  if (lStatRet != 0)
751  return 0;
752  else if (lInfo.st_mode & S_IFDIR)
753  return 1;
754  else
755  return 0;
756  }
757  else if (_stat(aDirPath, &lInfo) != 0)
758  return 0;
759  else if (lInfo.st_mode & S_IFDIR)
760  return 1;
761  else
762  return 0;
763 }
764 
765 
766 static int fileExists(char const * const aFilePathAndName)
767 {
768  struct _stat lInfo;
769  wchar_t * lTmpWChar;
770  int lStatRet;
771  FILE * lIn;
772 
773  if (!aFilePathAndName || !strlen(aFilePathAndName))
774  {
775  return 0;
776  }
777 
778  if (tinyfd_winUtf8)
779  {
780  lTmpWChar = utf8to16(aFilePathAndName);
781  lStatRet = _wstat(lTmpWChar, &lInfo);
782  free(lTmpWChar);
783  if (lStatRet != 0)
784  return 0;
785  else if (lInfo.st_mode & _S_IFREG)
786  return 1;
787  else
788  return 0;
789  }
790  else
791  {
792  lIn = fopen(aFilePathAndName, "r");
793  if (!lIn)
794  {
795  return 0;
796  }
797  fclose(lIn);
798  return 1;
799  }
800 }
801 
802 static int replaceWchar(wchar_t * const aString,
803  wchar_t const aOldChr,
804  wchar_t const aNewChr)
805 {
806  wchar_t * p;
807  int lRes = 0;
808 
809  if (!aString)
810  {
811  return 0;
812  }
813 
814  if (aOldChr == aNewChr)
815  {
816  return 0;
817  }
818 
819  p = aString;
820  while ((p = wcsrchr(p, aOldChr)))
821  {
822  *p = aNewChr;
823 #ifdef TINYFD_NOCCSUNICODE
824  p++;
825 #endif
826  p++;
827  lRes = 1;
828  }
829  return lRes;
830 }
831 
832 #endif /* TINYFD_NOLIB */
833 #endif /* _WIN32 */
834 
835 /* source and destination can be the same or ovelap*/
836 static char const * ensureFilesExist(char * const aDestination,
837  char const * const aSourcePathsAndNames)
838 {
839  char * lDestination = aDestination;
840  char const * p;
841  char const * p2;
842  size_t lLen;
843 
844  if (!aSourcePathsAndNames)
845  {
846  return NULL;
847  }
848  lLen = strlen(aSourcePathsAndNames);
849  if (!lLen)
850  {
851  return NULL;
852  }
853 
854  p = aSourcePathsAndNames;
855  while ((p2 = strchr(p, '|')) != NULL)
856  {
857  lLen = p2 - p;
858  memmove(lDestination, p, lLen);
859  lDestination[lLen] = '\0';
860  if (fileExists(lDestination))
861  {
862  lDestination += lLen;
863  *lDestination = '|';
864  lDestination++;
865  }
866  p = p2 + 1;
867  }
868  if (fileExists(p))
869  {
870  lLen = strlen(p);
871  memmove(lDestination, p, lLen);
872  lDestination[lLen] = '\0';
873  }
874  else
875  {
876  *(lDestination - 1) = '\0';
877  }
878  return aDestination;
879 }
880 
881 #ifdef _WIN32
882 #ifndef TINYFD_NOLIB
883 
884 static int __stdcall EnumThreadWndProc(HWND hwnd, LPARAM lParam)
885 {
886  wchar_t lTitleName[MAX_PATH];
887  GetWindowTextW(hwnd, lTitleName, MAX_PATH);
888  /* wprintf(L"lTitleName %ls \n", lTitleName); */
889  if (wcscmp(L"tinyfiledialogsTopWindow", lTitleName) == 0)
890  {
891  SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
892  return 0;
893  }
894  return 1;
895 }
896 
897 
898 static void hiddenConsoleW(wchar_t const * const aString, wchar_t const * const aDialogTitle, int const aInFront)
899 {
900  STARTUPINFOW StartupInfo;
901  PROCESS_INFORMATION ProcessInfo;
902 
903  if (!aString || !wcslen(aString) ) return;
904 
905  memset(&StartupInfo, 0, sizeof(StartupInfo));
906  StartupInfo.cb = sizeof(STARTUPINFOW);
907  StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
908  StartupInfo.wShowWindow = SW_HIDE;
909 
910  if (!CreateProcessW(NULL, (LPWSTR)aString, NULL, NULL, FALSE,
911  CREATE_NEW_CONSOLE, NULL, NULL,
912  &StartupInfo, &ProcessInfo))
913  {
914  return; /* GetLastError(); */
915  }
916 
917  WaitForInputIdle(ProcessInfo.hProcess, INFINITE);
918  if (aInFront)
919  {
920  while (EnumWindows(EnumThreadWndProc, (LPARAM)NULL)) {}
921  SetWindowTextW(GetForegroundWindow(), aDialogTitle);
922  }
923  WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
924  CloseHandle(ProcessInfo.hThread);
925  CloseHandle(ProcessInfo.hProcess);
926 }
927 
928 
929 int tinyfd_messageBoxW(
930  wchar_t const * const aTitle, /* NULL or "" */
931  wchar_t const * const aMessage, /* NULL or "" may contain \n and \t */
932  wchar_t const * const aDialogType, /* "ok" "okcancel" "yesno" "yesnocancel" */
933  wchar_t const * const aIconType, /* "info" "warning" "error" "question" */
934  int const aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */
935 {
936  int lBoxReturnValue;
937  UINT aCode;
938 
939  if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return 1; }
940 
941  if (aIconType && !wcscmp(L"warning", aIconType))
942  {
943  aCode = MB_ICONWARNING;
944  }
945  else if (aIconType && !wcscmp(L"error", aIconType))
946  {
947  aCode = MB_ICONERROR;
948  }
949  else if (aIconType && !wcscmp(L"question", aIconType))
950  {
951  aCode = MB_ICONQUESTION;
952  }
953  else
954  {
955  aCode = MB_ICONINFORMATION;
956  }
957 
958  if (aDialogType && !wcscmp(L"okcancel", aDialogType))
959  {
960  aCode += MB_OKCANCEL;
961  if (!aDefaultButton)
962  {
963  aCode += MB_DEFBUTTON2;
964  }
965  }
966  else if (aDialogType && !wcscmp(L"yesno", aDialogType))
967  {
968  aCode += MB_YESNO;
969  if (!aDefaultButton)
970  {
971  aCode += MB_DEFBUTTON2;
972  }
973  }
974  else
975  {
976  aCode += MB_OK;
977  }
978 
979  aCode += MB_TOPMOST;
980 
981  lBoxReturnValue = MessageBoxW(GetForegroundWindow(), aMessage, aTitle, aCode);
982  if (((aDialogType
983  && wcscmp(L"okcancel", aDialogType)
984  && wcscmp(L"yesno", aDialogType)))
985  || (lBoxReturnValue == IDOK)
986  || (lBoxReturnValue == IDYES))
987  {
988  return 1;
989  }
990  else
991  {
992  return 0;
993  }
994 }
995 
996 
997 static int messageBoxWinGui8(
998  char const * const aTitle, /* NULL or "" */
999  char const * const aMessage, /* NULL or "" may contain \n and \t */
1000  char const * const aDialogType, /* "ok" "okcancel" "yesno" "yesnocancel" */
1001  char const * const aIconType, /* "info" "warning" "error" "question" */
1002  int const aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */
1003 {
1004  int lIntRetVal;
1005  wchar_t * lTitle;
1006  wchar_t * lMessage;
1007  wchar_t * lDialogType;
1008  wchar_t * lIconType;
1009 
1010  lTitle = utf8to16(aTitle);
1011  lMessage = utf8to16(aMessage);
1012  lDialogType = utf8to16(aDialogType);
1013  lIconType = utf8to16(aIconType);
1014 
1015  lIntRetVal = tinyfd_messageBoxW(lTitle, lMessage,
1016  lDialogType, lIconType, aDefaultButton );
1017 
1018  free(lTitle);
1019  free(lMessage);
1020  free(lDialogType);
1021  free(lIconType);
1022 
1023  return lIntRetVal ;
1024 }
1025 
1026 
1027 /* return has only meaning for tinyfd_query */
1028 int tinyfd_notifyPopupW(
1029  wchar_t const * const aTitle, /* NULL or L"" */
1030  wchar_t const * const aMessage, /* NULL or L"" may contain \n \t */
1031  wchar_t const * const aIconType) /* L"info" L"warning" L"error" */
1032 {
1033  wchar_t * lDialogString;
1034  size_t lTitleLen;
1035  size_t lMessageLen;
1036  size_t lDialogStringLen;
1037 
1038  if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return 1; }
1039 
1040  lTitleLen = aTitle ? wcslen(aTitle) : 0;
1041  lMessageLen = aMessage ? wcslen(aMessage) : 0;
1042  lDialogStringLen = 3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen;
1043  lDialogString = (wchar_t *)malloc(2 * lDialogStringLen);
1044 
1045  wcscpy(lDialogString, L"powershell.exe -command \"\
1046 function Show-BalloonTip {\
1047 [cmdletbinding()] \
1048 param( \
1049 [string]$Title = ' ', \
1050 [string]$Message = ' ', \
1051 [ValidateSet('info', 'warning', 'error')] \
1052 [string]$IconType = 'info');\
1053 [system.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null ; \
1054 $balloon = New-Object System.Windows.Forms.NotifyIcon ; \
1055 $path = Get-Process -id $pid | Select-Object -ExpandProperty Path ; \
1056 $icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path) ;");
1057 
1058  wcscat(lDialogString, L"\
1059 $balloon.Icon = $icon ; \
1060 $balloon.BalloonTipIcon = $IconType ; \
1061 $balloon.BalloonTipText = $Message ; \
1062 $balloon.BalloonTipTitle = $Title ; \
1063 $balloon.Text = 'lalala' ; \
1064 $balloon.Visible = $true ; \
1065 $balloon.ShowBalloonTip(5000)};\
1066 Show-BalloonTip");
1067 
1068  if (aTitle && wcslen(aTitle))
1069  {
1070  wcscat(lDialogString, L" -Title '");
1071  wcscat(lDialogString, aTitle);
1072  wcscat(lDialogString, L"'");
1073  }
1074  if (aMessage && wcslen(aMessage))
1075  {
1076  wcscat(lDialogString, L" -Message '");
1077  wcscat(lDialogString, aMessage);
1078  wcscat(lDialogString, L"'");
1079  }
1080  if (aMessage && wcslen(aIconType))
1081  {
1082  wcscat(lDialogString, L" -IconType '");
1083  wcscat(lDialogString, aIconType);
1084  wcscat(lDialogString, L"'");
1085  }
1086  wcscat(lDialogString, L"\"");
1087 
1088  /* wprintf ( L"lDialogString: %ls\n" , lDialogString ) ; */
1089 
1090  hiddenConsoleW(lDialogString, aTitle, 0);
1091  free(lDialogString);
1092  return 1;
1093 }
1094 
1095 
1096 static int notifyWinGui(
1097  char const * const aTitle, /* NULL or "" */
1098  char const * const aMessage, /* NULL or "" may NOT contain \n nor \t */
1099  char const * const aIconType)
1100 {
1101  wchar_t * lTitle;
1102  wchar_t * lMessage;
1103  wchar_t * lIconType;
1104 
1105  if (tinyfd_winUtf8)
1106  {
1107  lTitle = utf8to16(aTitle);
1108  lMessage = utf8to16(aMessage);
1109  lIconType = utf8to16(aIconType);
1110  }
1111  else
1112  {
1113  lTitle = mbcsTo16(aTitle);
1114  lMessage = mbcsTo16(aMessage);
1115  lIconType = mbcsTo16(aIconType);
1116  }
1117 
1118  tinyfd_notifyPopupW( lTitle, lMessage, lIconType);
1119 
1120  free(lTitle);
1121  free(lMessage);
1122  free(lIconType);
1123  return 1;
1124 }
1125 
1126 
1127 wchar_t const * tinyfd_inputBoxW(
1128  wchar_t const * const aTitle, /* NULL or L"" */
1129  wchar_t const * const aMessage, /* NULL or L"" may NOT contain \n nor \t */
1130  wchar_t const * const aDefaultInput) /* L"" , if NULL it's a passwordBox */
1131 {
1132  static wchar_t lBuff[MAX_PATH_OR_CMD];
1133  wchar_t * lDialogString;
1134  FILE * lIn;
1135  FILE * lFile;
1136  int lResult;
1137  size_t lTitleLen;
1138  size_t lMessageLen;
1139  size_t lDialogStringLen;
1140 
1141  if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t const *)1; }
1142 
1143  lTitleLen = aTitle ? wcslen(aTitle) : 0 ;
1144  lMessageLen = aMessage ? wcslen(aMessage) : 0 ;
1145  lDialogStringLen = 3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen;
1146  lDialogString = (wchar_t *)malloc(2 * lDialogStringLen);
1147 
1148  if (aDefaultInput)
1149  {
1150  swprintf(lDialogString,
1151 #if !defined(__BORLANDC__) && !defined(__TINYC__) && ( !defined(__GNUC__) || (__GNUC__) >= 5 )
1152  lDialogStringLen,
1153 #endif
1154  L"%ls\\AppData\\Local\\Temp\\tinyfd.vbs", _wgetenv(L"USERPROFILE"));
1155  }
1156  else
1157  {
1158  swprintf(lDialogString,
1159 #if !defined(__BORLANDC__) && !defined(__TINYC__) && ( !defined(__GNUC__) || (__GNUC__) >= 5 )
1160  lDialogStringLen,
1161 #endif
1162  L"%ls\\AppData\\Local\\Temp\\tinyfd.hta", _wgetenv(L"USERPROFILE"));
1163  }
1164  lIn = _wfopen(lDialogString, L"w");
1165  if (!lIn)
1166  {
1167  free(lDialogString);
1168  return NULL;
1169  }
1170 
1171  if ( aDefaultInput )
1172  {
1173  wcscpy(lDialogString, L"Dim result:result=InputBox(\"");
1174  if (aMessage && wcslen(aMessage))
1175  {
1176  wcscpy(lBuff, aMessage);
1177  replaceWchar(lBuff, L'\n', L' ');
1178  wcscat(lDialogString, lBuff);
1179  }
1180  wcscat(lDialogString, L"\",\"tinyfiledialogsTopWindow\",\"");
1181  if (aDefaultInput && wcslen(aDefaultInput))
1182  {
1183  wcscpy(lBuff, aDefaultInput);
1184  replaceWchar(lBuff, L'\n', L' ');
1185  wcscat(lDialogString, lBuff);
1186  }
1187  wcscat(lDialogString, L"\"):If IsEmpty(result) then:WScript.Echo 0");
1188  wcscat(lDialogString, L":Else: WScript.Echo \"1\" & result : End If");
1189  }
1190  else
1191  {
1192  wcscpy(lDialogString, L"\n\
1193 <html>\n\
1194 <head>\n\
1195 <title>");
1196 
1197  wcscat(lDialogString, L"tinyfiledialogsTopWindow");
1198  wcscat(lDialogString, L"</title>\n\
1199 <HTA:APPLICATION\n\
1200 ID = 'tinyfdHTA'\n\
1201 APPLICATIONNAME = 'tinyfd_inputBox'\n\
1202 MINIMIZEBUTTON = 'no'\n\
1203 MAXIMIZEBUTTON = 'no'\n\
1204 BORDER = 'dialog'\n\
1205 SCROLL = 'no'\n\
1206 SINGLEINSTANCE = 'yes'\n\
1207 WINDOWSTATE = 'hidden'>\n\
1208 \n\
1209 <script language = 'VBScript'>\n\
1210 \n\
1211 intWidth = Screen.Width/4\n\
1212 intHeight = Screen.Height/6\n\
1213 ResizeTo intWidth, intHeight\n\
1214 MoveTo((Screen.Width/2)-(intWidth/2)),((Screen.Height/2)-(intHeight/2))\n\
1215 result = 0\n\
1216 \n\
1217 Sub Window_onLoad\n\
1218 txt_input.Focus\n\
1219 End Sub\n\
1220 \n");
1221 
1222  wcscat(lDialogString, L"\
1223 Sub Window_onUnload\n\
1224 Set objFSO = CreateObject(\"Scripting.FileSystemObject\")\n\
1225 Set oShell = CreateObject(\"WScript.Shell\")\n\
1226 strHomeFolder = oShell.ExpandEnvironmentStrings(\"%USERPROFILE%\")\n\
1227 Set objFile = objFSO.CreateTextFile(strHomeFolder & \"\\AppData\\Local\\Temp\\tinyfd.txt\",True,True)\n\
1228 If result = 1 Then\n\
1229 objFile.Write 1 & txt_input.Value\n\
1230 Else\n\
1231 objFile.Write 0\n\
1232 End If\n\
1233 objFile.Close\n\
1234 End Sub\n\
1235 \n\
1236 Sub Run_ProgramOK\n\
1237 result = 1\n\
1238 window.Close\n\
1239 End Sub\n\
1240 \n\
1241 Sub Run_ProgramCancel\n\
1242 window.Close\n\
1243 End Sub\n\
1244 \n");
1245 
1246  wcscat(lDialogString, L"Sub Default_Buttons\n\
1247 If Window.Event.KeyCode = 13 Then\n\
1248 btn_OK.Click\n\
1249 ElseIf Window.Event.KeyCode = 27 Then\n\
1250 btn_Cancel.Click\n\
1251 End If\n\
1252 End Sub\n\
1253 \n\
1254 </script>\n\
1255 </head>\n\
1256 <body style = 'background-color:#EEEEEE' onkeypress = 'vbs:Default_Buttons' align = 'top'>\n\
1257 <table width = '100%' height = '80%' align = 'center' border = '0'>\n\
1258 <tr border = '0'>\n\
1259 <td align = 'left' valign = 'middle' style='Font-Family:Arial'>\n");
1260 
1261  wcscat(lDialogString, aMessage ? aMessage : L"");
1262 
1263  wcscat(lDialogString, L"\n\
1264 </td>\n\
1265 <td align = 'right' valign = 'middle' style = 'margin-top: 0em'>\n\
1266 <table align = 'right' style = 'margin-right: 0em;'>\n\
1267 <tr align = 'right' style = 'margin-top: 5em;'>\n\
1268 <input type = 'button' value = 'OK' name = 'btn_OK' onClick = 'vbs:Run_ProgramOK' style = 'width: 5em; margin-top: 2em;'><br>\n\
1269 <input type = 'button' value = 'Cancel' name = 'btn_Cancel' onClick = 'vbs:Run_ProgramCancel' style = 'width: 5em;'><br><br>\n\
1270 </tr>\n\
1271 </table>\n\
1272 </td>\n\
1273 </tr>\n\
1274 </table>\n");
1275 
1276  wcscat(lDialogString, L"<table width = '100%' height = '100%' align = 'center' border = '0'>\n\
1277 <tr>\n\
1278 <td align = 'left' valign = 'top'>\n\
1279 <input type = 'password' id = 'txt_input'\n\
1280 name = 'txt_input' value = '' style = 'float:left;width:100%' ><BR>\n\
1281 </td>\n\
1282 </tr>\n\
1283 </table>\n\
1284 </body>\n\
1285 </html>\n\
1286 " ) ;
1287  }
1288  fputws(lDialogString, lIn);
1289  fclose(lIn);
1290 
1291  if (aDefaultInput)
1292  {
1293  swprintf(lDialogString,
1294 #if !defined(__BORLANDC__) && !defined(__TINYC__) && ( !defined(__GNUC__) || (__GNUC__) >= 5 )
1295  lDialogStringLen,
1296 #endif
1297  L"%ls\\AppData\\Local\\Temp\\tinyfd.txt",_wgetenv(L"USERPROFILE"));
1298 
1299 #ifdef TINYFD_NOCCSUNICODE
1300  lFile = _wfopen(lDialogString, L"w");
1301  fputc(0xFF, lFile);
1302  fputc(0xFE, lFile);
1303 #else
1304  lFile = _wfopen(lDialogString, L"wt, ccs=UNICODE"); /*or ccs=UTF-16LE*/
1305 #endif
1306  fclose(lFile);
1307 
1308  wcscpy(lDialogString, L"cmd.exe /c cscript.exe //U //Nologo ");
1309  wcscat(lDialogString, L"\"%USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.vbs\" ");
1310  wcscat(lDialogString, L">> \"%USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.txt\"");
1311  }
1312  else
1313  {
1314  wcscpy(lDialogString,
1315  L"cmd.exe /c mshta.exe \"%USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.hta\"");
1316  }
1317 
1318  /* wprintf ( "lDialogString: %ls\n" , lDialogString ) ; */
1319 
1320  hiddenConsoleW(lDialogString, aTitle, 1);
1321 
1322  swprintf(lDialogString,
1323 #if !defined(__BORLANDC__) && !defined(__TINYC__) && ( !defined(__GNUC__) || (__GNUC__) >= 5 )
1324  lDialogStringLen,
1325 #endif
1326  L"%ls\\AppData\\Local\\Temp\\tinyfd.txt", _wgetenv(L"USERPROFILE"));
1327  /* wprintf(L"lDialogString: %ls\n", lDialogString); */
1328 #ifdef TINYFD_NOCCSUNICODE
1329  if (!(lIn = _wfopen(lDialogString, L"r")))
1330 #else
1331  if (!(lIn = _wfopen(lDialogString, L"rt, ccs=UNICODE"))) /*or ccs=UTF-16LE*/
1332 #endif
1333  {
1334  _wremove(lDialogString);
1335  free(lDialogString);
1336  return NULL;
1337  }
1338 
1339  memset(lBuff, 0, MAX_PATH_OR_CMD);
1340 
1341 #ifdef TINYFD_NOCCSUNICODE
1342  fgets((char *)lBuff, 2*MAX_PATH_OR_CMD, lIn);
1343 #else
1344  fgetws(lBuff, MAX_PATH_OR_CMD, lIn);
1345 #endif
1346  fclose(lIn);
1347  wipefileW(lDialogString);
1348  _wremove(lDialogString);
1349 
1350  if (aDefaultInput)
1351  {
1352  swprintf(lDialogString,
1353 #if !defined(__BORLANDC__) && !defined(__TINYC__) && ( !defined(__GNUC__) || (__GNUC__) >= 5 )
1354  lDialogStringLen,
1355 #endif
1356  L"%ls\\AppData\\Local\\Temp\\tinyfd.vbs",
1357  _wgetenv(L"USERPROFILE"));
1358  }
1359  else
1360  {
1361  swprintf(lDialogString,
1362 #if !defined(__BORLANDC__) && !defined(__TINYC__) && ( !defined(__GNUC__) || (__GNUC__) >= 5 )
1363  lDialogStringLen,
1364 #endif
1365  L"%ls\\AppData\\Local\\Temp\\tinyfd.hta",
1366  _wgetenv(L"USERPROFILE"));
1367  }
1368  _wremove(lDialogString);
1369  free(lDialogString);
1370  /* wprintf( L"lBuff: %ls\n" , lBuff ) ; */
1371 #ifdef TINYFD_NOCCSUNICODE
1372  lResult = !wcsncmp(lBuff+1, L"1", 1);
1373 #else
1374  lResult = !wcsncmp(lBuff, L"1", 1);
1375 #endif
1376 
1377  /* printf( "lResult: %d \n" , lResult ) ; */
1378  if (!lResult)
1379  {
1380  return NULL ;
1381  }
1382 
1383  /* wprintf( "lBuff+1: %ls\n" , lBuff+1 ) ; */
1384 
1385 #ifdef TINYFD_NOCCSUNICODE
1386  if (aDefaultInput)
1387  {
1388  lDialogStringLen = wcslen(lBuff) ;
1389  lBuff[lDialogStringLen - 1] = L'\0';
1390  lBuff[lDialogStringLen - 2] = L'\0';
1391  }
1392  return lBuff + 2;
1393 #else
1394  if (aDefaultInput) lBuff[wcslen(lBuff) - 1] = L'\0';
1395  return lBuff + 1;
1396 #endif
1397 }
1398 
1399 
1400 static char const * inputBoxWinGui(
1401  char * const aoBuff,
1402  char const * const aTitle, /* NULL or "" */
1403  char const * const aMessage, /* NULL or "" may NOT contain \n nor \t */
1404  char const * const aDefaultInput) /* "" , if NULL it's a passwordBox */
1405 {
1406  wchar_t * lTitle;
1407  wchar_t * lMessage;
1408  wchar_t * lDefaultInput;
1409  wchar_t const * lTmpWChar;
1410  char * lTmpChar;
1411 
1412  if (tinyfd_winUtf8)
1413  {
1414  lTitle = utf8to16(aTitle);
1415  lMessage = utf8to16(aMessage);
1416  lDefaultInput = utf8to16(aDefaultInput);
1417  }
1418  else
1419  {
1420  lTitle = mbcsTo16(aTitle);
1421  lMessage = mbcsTo16(aMessage);
1422  lDefaultInput = mbcsTo16(aDefaultInput);
1423  }
1424 
1425  lTmpWChar = tinyfd_inputBoxW( lTitle, lMessage, lDefaultInput);
1426 
1427  free(lTitle);
1428  free(lMessage);
1429  free(lDefaultInput);
1430 
1431  if (!lTmpWChar)
1432  {
1433  return NULL;
1434  }
1435 
1436  if (tinyfd_winUtf8)
1437  {
1438  lTmpChar = utf16to8(lTmpWChar);
1439  }
1440  else
1441  {
1442  lTmpChar = utf16toMbcs(lTmpWChar);
1443  }
1444  strcpy(aoBuff, lTmpChar);
1445  free(lTmpChar);
1446 
1447  return aoBuff;
1448 }
1449 
1450 
1451 wchar_t const * tinyfd_saveFileDialogW(
1452  wchar_t const * const aTitle, /* NULL or "" */
1453  wchar_t const * const aDefaultPathAndFile, /* NULL or "" */
1454  int const aNumOfFilterPatterns, /* 0 */
1455  wchar_t const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */
1456  wchar_t const * const aSingleFilterDescription) /* NULL or "image files" */
1457 {
1458  static wchar_t lBuff[MAX_PATH_OR_CMD];
1459  wchar_t lDirname[MAX_PATH_OR_CMD];
1460  wchar_t lDialogString[MAX_PATH_OR_CMD];
1461  wchar_t lFilterPatterns[MAX_PATH_OR_CMD] = L"";
1462  wchar_t * p;
1463  wchar_t * lRetval;
1464  wchar_t const * ldefExt = NULL;
1465  int i;
1466  HRESULT lHResult;
1467  OPENFILENAMEW ofn = {0};
1468 
1469  if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t const *)1; }
1470 
1471  lHResult = CoInitializeEx(NULL, 0);
1472 
1473  getPathWithoutFinalSlashW(lDirname, aDefaultPathAndFile);
1474  getLastNameW(lBuff, aDefaultPathAndFile);
1475 
1476  if (aNumOfFilterPatterns > 0)
1477  {
1478  ldefExt = aFilterPatterns[0];
1479 
1480  if (aSingleFilterDescription && wcslen(aSingleFilterDescription))
1481  {
1482  wcscpy(lFilterPatterns, aSingleFilterDescription);
1483  wcscat(lFilterPatterns, L"\n");
1484  }
1485  wcscat(lFilterPatterns, aFilterPatterns[0]);
1486  for (i = 1; i < aNumOfFilterPatterns; i++)
1487  {
1488  wcscat(lFilterPatterns, L";");
1489  wcscat(lFilterPatterns, aFilterPatterns[i]);
1490  }
1491  wcscat(lFilterPatterns, L"\n");
1492  if (!(aSingleFilterDescription && wcslen(aSingleFilterDescription)))
1493  {
1494  wcscpy(lDialogString, lFilterPatterns);
1495  wcscat(lFilterPatterns, lDialogString);
1496  }
1497  wcscat(lFilterPatterns, L"All Files\n*.*\n");
1498  p = lFilterPatterns;
1499  while ((p = wcschr(p, L'\n')) != NULL)
1500  {
1501  *p = L'\0';
1502  p++;
1503  }
1504  }
1505 
1506  ofn.lStructSize = sizeof(OPENFILENAMEW);
1507  ofn.hwndOwner = GetForegroundWindow();
1508  ofn.hInstance = 0;
1509  ofn.lpstrFilter = lFilterPatterns && wcslen(lFilterPatterns) ? lFilterPatterns : NULL;
1510  ofn.lpstrCustomFilter = NULL;
1511  ofn.nMaxCustFilter = 0;
1512  ofn.nFilterIndex = 1;
1513  ofn.lpstrFile = lBuff;
1514 
1515  ofn.nMaxFile = MAX_PATH_OR_CMD;
1516  ofn.lpstrFileTitle = NULL;
1517  ofn.nMaxFileTitle = MAX_PATH_OR_CMD/2;
1518  ofn.lpstrInitialDir = lDirname && wcslen(lDirname) ? lDirname : NULL;
1519  ofn.lpstrTitle = aTitle && wcslen(aTitle) ? aTitle : NULL;
1520  ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST ;
1521  ofn.nFileOffset = 0;
1522  ofn.nFileExtension = 0;
1523  ofn.lpstrDefExt = ldefExt;
1524  ofn.lCustData = 0L;
1525  ofn.lpfnHook = NULL;
1526  ofn.lpTemplateName = NULL;
1527 
1528  if (GetSaveFileNameW(&ofn) == 0)
1529  {
1530  lRetval = NULL;
1531  }
1532  else
1533  {
1534  lRetval = lBuff;
1535  }
1536 
1537  if (lHResult == S_OK || lHResult == S_FALSE)
1538  {
1539  CoUninitialize();
1540  }
1541  return lRetval;
1542 }
1543 
1544 
1545 static char const * saveFileDialogWinGui8(
1546  char * const aoBuff,
1547  char const * const aTitle, /* NULL or "" */
1548  char const * const aDefaultPathAndFile, /* NULL or "" */
1549  int const aNumOfFilterPatterns, /* 0 */
1550  char const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */
1551  char const * const aSingleFilterDescription) /* NULL or "image files" */
1552 {
1553  wchar_t * lTitle;
1554  wchar_t * lDefaultPathAndFile;
1555  wchar_t * lSingleFilterDescription;
1556  wchar_t * * lFilterPatterns;
1557  wchar_t const * lTmpWChar;
1558  char * lTmpChar;
1559  int i ;
1560 
1561  lFilterPatterns = (wchar_t **) malloc(aNumOfFilterPatterns*sizeof(wchar_t *));
1562  for (i = 0; i < aNumOfFilterPatterns; i++)
1563  {
1564  lFilterPatterns[i] = utf8to16(aFilterPatterns[i]);
1565  }
1566 
1567  lTitle = utf8to16(aTitle);
1568  lDefaultPathAndFile = utf8to16(aDefaultPathAndFile);
1569  lSingleFilterDescription = utf8to16(aSingleFilterDescription);
1570 
1571  lTmpWChar = tinyfd_saveFileDialogW(
1572  lTitle,
1573  lDefaultPathAndFile,
1574  aNumOfFilterPatterns,
1575  (wchar_t const** ) /*stupid cast for gcc*/
1576  lFilterPatterns,
1577  lSingleFilterDescription);
1578 
1579  free(lTitle);
1580  free(lDefaultPathAndFile);
1581  free(lSingleFilterDescription);
1582  for (i = 0; i < aNumOfFilterPatterns; i++)
1583  {
1584  free(lFilterPatterns[i]);
1585  }
1586  free(lFilterPatterns);
1587 
1588  if (!lTmpWChar)
1589  {
1590  return NULL;
1591  }
1592 
1593  lTmpChar = utf16to8(lTmpWChar);
1594  strcpy(aoBuff, lTmpChar);
1595  free(lTmpChar);
1596 
1597  return aoBuff;
1598 }
1599 
1600 
1601 wchar_t const * tinyfd_openFileDialogW(
1602  wchar_t const * const aTitle, /* NULL or "" */
1603  wchar_t const * const aDefaultPathAndFile, /* NULL or "" */
1604  int const aNumOfFilterPatterns, /* 0 */
1605  wchar_t const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */
1606  wchar_t const * const aSingleFilterDescription, /* NULL or "image files" */
1607  int const aAllowMultipleSelects) /* 0 or 1 */
1608 {
1609  static wchar_t lBuff[MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD];
1610 
1611  size_t lLengths[MAX_MULTIPLE_FILES];
1612  wchar_t lDirname[MAX_PATH_OR_CMD];
1613  wchar_t lFilterPatterns[MAX_PATH_OR_CMD] = L"";
1614  wchar_t lDialogString[MAX_PATH_OR_CMD];
1615  wchar_t * lPointers[MAX_MULTIPLE_FILES];
1616  wchar_t * lRetval, * p;
1617  int i, j;
1618  size_t lBuffLen;
1619  HRESULT lHResult;
1620  OPENFILENAMEW ofn = { 0 };
1621 
1622  if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t const *)1; }
1623 
1624  lHResult = CoInitializeEx(NULL, 0);
1625 
1626  getPathWithoutFinalSlashW(lDirname, aDefaultPathAndFile);
1627  getLastNameW(lBuff, aDefaultPathAndFile);
1628 
1629  if (aNumOfFilterPatterns > 0)
1630  {
1631  if (aSingleFilterDescription && wcslen(aSingleFilterDescription))
1632  {
1633  wcscpy(lFilterPatterns, aSingleFilterDescription);
1634  wcscat(lFilterPatterns, L"\n");
1635  }
1636  wcscat(lFilterPatterns, aFilterPatterns[0]);
1637  for (i = 1; i < aNumOfFilterPatterns; i++)
1638  {
1639  wcscat(lFilterPatterns, L";");
1640  wcscat(lFilterPatterns, aFilterPatterns[i]);
1641  }
1642  wcscat(lFilterPatterns, L"\n");
1643  if (!(aSingleFilterDescription && wcslen(aSingleFilterDescription)))
1644  {
1645  wcscpy(lDialogString, lFilterPatterns);
1646  wcscat(lFilterPatterns, lDialogString);
1647  }
1648  wcscat(lFilterPatterns, L"All Files\n*.*\n");
1649  p = lFilterPatterns;
1650  while ((p = wcschr(p, L'\n')) != NULL)
1651  {
1652  *p = L'\0';
1653  p++;
1654  }
1655  }
1656 
1657  ofn.lStructSize = sizeof(OPENFILENAME);
1658  ofn.hwndOwner = GetForegroundWindow();
1659  ofn.hInstance = 0;
1660  ofn.lpstrFilter = lFilterPatterns && wcslen(lFilterPatterns) ? lFilterPatterns : NULL;
1661  ofn.lpstrCustomFilter = NULL;
1662  ofn.nMaxCustFilter = 0;
1663  ofn.nFilterIndex = 1;
1664  ofn.lpstrFile = lBuff;
1665  ofn.nMaxFile = MAX_PATH_OR_CMD;
1666  ofn.lpstrFileTitle = NULL;
1667  ofn.nMaxFileTitle = MAX_PATH_OR_CMD / 2;
1668  ofn.lpstrInitialDir = lDirname && wcslen(lDirname) ? lDirname : NULL;
1669  ofn.lpstrTitle = aTitle && wcslen(aTitle) ? aTitle : NULL;
1670  ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
1671  ofn.nFileOffset = 0;
1672  ofn.nFileExtension = 0;
1673  ofn.lpstrDefExt = NULL;
1674  ofn.lCustData = 0L;
1675  ofn.lpfnHook = NULL;
1676  ofn.lpTemplateName = NULL;
1677 
1678  if (aAllowMultipleSelects)
1679  {
1680  ofn.Flags |= OFN_ALLOWMULTISELECT;
1681  }
1682 
1683  if (GetOpenFileNameW(&ofn) == 0)
1684  {
1685  lRetval = NULL;
1686  }
1687  else
1688  {
1689  lBuffLen = wcslen(lBuff);
1690  lPointers[0] = lBuff + lBuffLen + 1;
1691  if (!aAllowMultipleSelects || (lPointers[0][0] == L'\0'))
1692  {
1693  lRetval = lBuff;
1694  }
1695  else
1696  {
1697  i = 0;
1698  do
1699  {
1700  lLengths[i] = wcslen(lPointers[i]);
1701  lPointers[i + 1] = lPointers[i] + lLengths[i] + 1;
1702  i++;
1703  } while (lPointers[i][0] != L'\0');
1704  i--;
1705  p = lBuff + MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD - 1;
1706  *p = L'\0';
1707  for (j = i; j >= 0; j--)
1708  {
1709  p -= lLengths[j];
1710  memmove(p, lPointers[j], lLengths[j]*sizeof(wchar_t));
1711  p--;
1712  *p = L'\\';
1713  p -= lBuffLen;
1714  memmove(p, lBuff, lBuffLen*sizeof(wchar_t));
1715  p--;
1716  *p = L'|';
1717  }
1718  p++;
1719  lRetval = p;
1720  }
1721  }
1722 
1723  if (lHResult == S_OK || lHResult == S_FALSE)
1724  {
1725  CoUninitialize();
1726  }
1727  return lRetval;
1728 }
1729 
1730 
1731 static char const * openFileDialogWinGui8(
1732  char * const aoBuff,
1733  char const * const aTitle, /* NULL or "" */
1734  char const * const aDefaultPathAndFile, /* NULL or "" */
1735  int const aNumOfFilterPatterns, /* 0 */
1736  char const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */
1737  char const * const aSingleFilterDescription, /* NULL or "image files" */
1738  int const aAllowMultipleSelects) /* 0 or 1 */
1739 {
1740  wchar_t * lTitle;
1741  wchar_t * lDefaultPathAndFile;
1742  wchar_t * lSingleFilterDescription;
1743  wchar_t * * lFilterPatterns;
1744  wchar_t const * lTmpWChar;
1745  char * lTmpChar;
1746  int i;
1747 
1748  lFilterPatterns = (wchar_t * *) malloc(aNumOfFilterPatterns*sizeof(wchar_t *));
1749  for (i = 0; i < aNumOfFilterPatterns; i++)
1750  {
1751  lFilterPatterns[i] = utf8to16(aFilterPatterns[i]);
1752  }
1753 
1754  lTitle = utf8to16(aTitle);
1755  lDefaultPathAndFile = utf8to16(aDefaultPathAndFile);
1756  lSingleFilterDescription = utf8to16(aSingleFilterDescription);
1757 
1758  lTmpWChar = tinyfd_openFileDialogW(
1759  lTitle,
1760  lDefaultPathAndFile,
1761  aNumOfFilterPatterns,
1762  (wchar_t const**) /*stupid cast for gcc*/
1763  lFilterPatterns,
1764  lSingleFilterDescription,
1765  aAllowMultipleSelects);
1766 
1767  free(lTitle);
1768  free(lDefaultPathAndFile);
1769  free(lSingleFilterDescription);
1770  for (i = 0; i < aNumOfFilterPatterns; i++)
1771  {
1772  free(lFilterPatterns[i]);
1773  }
1774  free(lFilterPatterns);
1775 
1776  if (!lTmpWChar)
1777  {
1778  return NULL;
1779  }
1780 
1781  lTmpChar = utf16to8(lTmpWChar);
1782  strcpy(aoBuff, lTmpChar);
1783  free(lTmpChar);
1784 
1785  return aoBuff;
1786 }
1787 
1788 #ifndef TINYFD_NOSELECTFOLDERWIN
1789 
1790 BOOL CALLBACK BrowseCallbackProc_enum(HWND hWndChild, LPARAM lParam)
1791 {
1792  char buf[255];
1793  GetClassNameA(hWndChild, buf, sizeof(buf));
1794  if (strcmp(buf, "SysTreeView32") == 0) {
1795  HTREEITEM hNode = TreeView_GetSelection(hWndChild);
1796  TreeView_EnsureVisible(hWndChild, hNode);
1797  return FALSE;
1798  }
1799  return TRUE;
1800 }
1801 
1802 
1803 
1804 BOOL CALLBACK BrowseCallbackProcW_enum(HWND hWndChild, LPARAM lParam)
1805 {
1806  wchar_t buf[255];
1807  GetClassNameW(hWndChild, buf, sizeof(buf));
1808  if (wcscmp(buf, L"SysTreeView32") == 0) {
1809  HTREEITEM hNode = TreeView_GetSelection(hWndChild);
1810  TreeView_EnsureVisible(hWndChild, hNode);
1811  return FALSE;
1812  }
1813  return TRUE;
1814 }
1815 
1816 static int __stdcall BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
1817 {
1818  switch (uMsg) {
1819  case BFFM_INITIALIZED:
1820  SendMessage(hwnd, BFFM_SETSELECTION, TRUE, pData);
1821  break;
1822  case BFFM_SELCHANGED:
1823  EnumChildWindows(hwnd, BrowseCallbackProc_enum, 0);
1824  }
1825  return 0;
1826 }
1827 
1828 
1829 static int __stdcall BrowseCallbackProcW(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
1830 {
1831  switch (uMsg) {
1832  case BFFM_INITIALIZED:
1833  SendMessage(hwnd, BFFM_SETSELECTIONW, TRUE, (LPARAM)pData);
1834  break;
1835  case BFFM_SELCHANGED:
1836  EnumChildWindows(hwnd, BrowseCallbackProcW_enum, 0);
1837  }
1838  return 0;
1839 }
1840 
1841 wchar_t const * tinyfd_selectFolderDialogW(
1842  wchar_t const * const aTitle, /* NULL or "" */
1843  wchar_t const * const aDefaultPath) /* NULL or "" */
1844 {
1845  static wchar_t lBuff[MAX_PATH_OR_CMD];
1846  wchar_t * lRetval;
1847 
1848  BROWSEINFOW bInfo;
1849  LPITEMIDLIST lpItem;
1850  HRESULT lHResult;
1851 
1852  if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t const *)1; }
1853 
1854  lHResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
1855 
1856  bInfo.hwndOwner = GetForegroundWindow();
1857  bInfo.pidlRoot = NULL;
1858  bInfo.pszDisplayName = lBuff;
1859  bInfo.lpszTitle = aTitle && wcslen(aTitle) ? aTitle : NULL;
1860  if (lHResult == S_OK || lHResult == S_FALSE)
1861  {
1862  bInfo.ulFlags = BIF_USENEWUI;
1863  }
1864  bInfo.lpfn = BrowseCallbackProcW;
1865  bInfo.lParam = (LPARAM)aDefaultPath;
1866  bInfo.iImage = -1;
1867 
1868  lpItem = SHBrowseForFolderW(&bInfo);
1869  if (!lpItem)
1870  {
1871  lRetval = NULL;
1872  }
1873  else
1874  {
1875  SHGetPathFromIDListW(lpItem, lBuff);
1876  lRetval = lBuff ;
1877  }
1878 
1879  if (lHResult == S_OK || lHResult == S_FALSE)
1880  {
1881  CoUninitialize();
1882  }
1883  return lRetval;
1884 }
1885 
1886 
1887 static char const * selectFolderDialogWinGui8(
1888  char * const aoBuff ,
1889  char const * const aTitle , /* NULL or "" */
1890  char const * const aDefaultPath ) /* NULL or "" */
1891 {
1892  wchar_t * lTitle;
1893  wchar_t * lDefaultPath;
1894  wchar_t const * lTmpWChar;
1895  char * lTmpChar;
1896 
1897  lTitle = utf8to16(aTitle);
1898  lDefaultPath = utf8to16(aDefaultPath);
1899 
1900  lTmpWChar = tinyfd_selectFolderDialogW(
1901  lTitle,
1902  lDefaultPath);
1903 
1904  free(lTitle);
1905  free(lDefaultPath);
1906  if (!lTmpWChar)
1907  {
1908  return NULL;
1909  }
1910 
1911  lTmpChar = utf16to8(lTmpWChar);
1912  strcpy(aoBuff, lTmpChar);
1913  free(lTmpChar);
1914 
1915  return aoBuff;
1916 }
1917 #endif /*TINYFD_NOSELECTFOLDERWIN*/
1918 
1919 
1920 wchar_t const * tinyfd_colorChooserW(
1921  wchar_t const * const aTitle, /* NULL or "" */
1922  wchar_t const * const aDefaultHexRGB, /* NULL or "#FF0000"*/
1923  unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */
1924  unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */
1925 {
1926  static wchar_t lResultHexRGB[8];
1927  CHOOSECOLORW cc;
1928  COLORREF crCustColors[16];
1929  unsigned char lDefaultRGB[3];
1930  int lRet;
1931 
1932  HRESULT lHResult;
1933 
1934  if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t const *)1; }
1935 
1936  lHResult = CoInitializeEx(NULL, 0);
1937 
1938  if (aDefaultHexRGB)
1939  {
1940  Hex2RGBW(aDefaultHexRGB, lDefaultRGB);
1941  }
1942  else
1943  {
1944  lDefaultRGB[0] = aDefaultRGB[0];
1945  lDefaultRGB[1] = aDefaultRGB[1];
1946  lDefaultRGB[2] = aDefaultRGB[2];
1947  }
1948 
1949  /* we can't use aTitle */
1950  cc.lStructSize = sizeof(CHOOSECOLOR);
1951  cc.hwndOwner = GetForegroundWindow();
1952  cc.hInstance = NULL;
1953  cc.rgbResult = RGB(lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]);
1954  cc.lpCustColors = crCustColors;
1955  cc.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ANYCOLOR ;
1956  cc.lCustData = 0;
1957  cc.lpfnHook = NULL;
1958  cc.lpTemplateName = NULL;
1959 
1960  lRet = ChooseColorW(&cc);
1961 
1962  if (!lRet)
1963  {
1964  return NULL;
1965  }
1966 
1967  aoResultRGB[0] = GetRValue(cc.rgbResult);
1968  aoResultRGB[1] = GetGValue(cc.rgbResult);
1969  aoResultRGB[2] = GetBValue(cc.rgbResult);
1970 
1971  RGB2HexW(aoResultRGB, lResultHexRGB);
1972 
1973  if (lHResult == S_OK || lHResult == S_FALSE)
1974  {
1975  CoUninitialize();
1976  }
1977 
1978  return lResultHexRGB;
1979 }
1980 
1981 
1982 static char const * colorChooserWinGui8(
1983  char const * const aTitle, /* NULL or "" */
1984  char const * const aDefaultHexRGB, /* NULL or "#FF0000"*/
1985  unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */
1986  unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */
1987 {
1988  static char lResultHexRGB[8];
1989 
1990  wchar_t * lTitle;
1991  wchar_t * lDefaultHexRGB;
1992  wchar_t const * lTmpWChar;
1993  char * lTmpChar;
1994 
1995  lTitle = utf8to16(aTitle);
1996  lDefaultHexRGB = utf8to16(aDefaultHexRGB);
1997 
1998  lTmpWChar = tinyfd_colorChooserW(
1999  lTitle,
2000  lDefaultHexRGB,
2001  aDefaultRGB,
2002  aoResultRGB );
2003 
2004  free(lTitle);
2005  free(lDefaultHexRGB);
2006  if (!lTmpWChar)
2007  {
2008  return NULL;
2009  }
2010 
2011  lTmpChar = utf16to8(lTmpWChar);
2012  strcpy(lResultHexRGB, lTmpChar);
2013  free(lTmpChar);
2014 
2015  return lResultHexRGB;
2016 }
2017 
2018 
2019 static int messageBoxWinGuiA(
2020  char const * const aTitle , /* NULL or "" */
2021  char const * const aMessage , /* NULL or "" may contain \n and \t */
2022  char const * const aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */
2023  char const * const aIconType , /* "info" "warning" "error" "question" */
2024  int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */
2025 {
2026  int lBoxReturnValue;
2027  UINT aCode ;
2028 
2029  if ( aIconType && ! strcmp( "warning" , aIconType ) )
2030  {
2031  aCode = MB_ICONWARNING ;
2032  }
2033  else if ( aIconType && ! strcmp("error", aIconType))
2034  {
2035  aCode = MB_ICONERROR ;
2036  }
2037  else if ( aIconType && ! strcmp("question", aIconType))
2038  {
2039  aCode = MB_ICONQUESTION ;
2040  }
2041  else
2042  {
2043  aCode = MB_ICONINFORMATION ;
2044  }
2045 
2046  if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) )
2047  {
2048  aCode += MB_OKCANCEL ;
2049  if ( ! aDefaultButton )
2050  {
2051  aCode += MB_DEFBUTTON2 ;
2052  }
2053  }
2054  else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) )
2055  {
2056  aCode += MB_YESNO ;
2057  if ( ! aDefaultButton )
2058  {
2059  aCode += MB_DEFBUTTON2 ;
2060  }
2061  }
2062  else if (aDialogType && !strcmp("yesnocancel", aDialogType))
2063  {
2064  aCode += MB_YESNOCANCEL;
2065  if (!aDefaultButton)
2066  {
2067  aCode += MB_DEFBUTTON3;
2068  }
2069  else if (aDefaultButton == 2)
2070  {
2071  aCode += MB_DEFBUTTON2;
2072  }
2073  }
2074  else
2075  {
2076  aCode += MB_OK ;
2077  }
2078 
2079  aCode += MB_TOPMOST;
2080 
2081  lBoxReturnValue = MessageBoxA(GetForegroundWindow(), aMessage, aTitle, aCode);
2082 
2083  if (((aDialogType && !strcmp("yesnocancel", aDialogType))
2084  && (lBoxReturnValue == IDNO)))
2085  {
2086  return 2;
2087  }
2088 
2089  if ( ( ( aDialogType
2090  && strcmp("yesnocancel", aDialogType)
2091  && strcmp("okcancel", aDialogType)
2092  && strcmp("yesno", aDialogType)))
2093  || (lBoxReturnValue == IDOK)
2094  || (lBoxReturnValue == IDYES) )
2095  {
2096  return 1 ;
2097  }
2098  else
2099  {
2100  return 0 ;
2101  }
2102 }
2103 
2104 
2105 static char const * saveFileDialogWinGuiA(
2106  char * const aoBuff ,
2107  char const * const aTitle , /* NULL or "" */
2108  char const * const aDefaultPathAndFile , /* NULL or "" */
2109  int const aNumOfFilterPatterns , /* 0 */
2110  char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */
2111  char const * const aSingleFilterDescription ) /* NULL or "image files" */
2112 {
2113  char lDirname [MAX_PATH_OR_CMD] ;
2114  char lDialogString[MAX_PATH_OR_CMD];
2115  char lFilterPatterns[MAX_PATH_OR_CMD] = "";
2116  int i ;
2117  char * p;
2118  char * lRetval;
2119  HRESULT lHResult;
2120  char const * ldefExt = NULL;
2121  OPENFILENAMEA ofn = { 0 };
2122 
2123  lHResult = CoInitializeEx(NULL,0);
2124 
2125  getPathWithoutFinalSlash(lDirname, aDefaultPathAndFile);
2126  getLastName(aoBuff, aDefaultPathAndFile);
2127 
2128  if (aNumOfFilterPatterns > 0)
2129  {
2130  ldefExt = aFilterPatterns[0];
2131 
2132  if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )
2133  {
2134  strcpy(lFilterPatterns, aSingleFilterDescription);
2135  strcat(lFilterPatterns, "\n");
2136  }
2137  strcat(lFilterPatterns, aFilterPatterns[0]);
2138  for (i = 1; i < aNumOfFilterPatterns; i++)
2139  {
2140  strcat(lFilterPatterns, ";");
2141  strcat(lFilterPatterns, aFilterPatterns[i]);
2142  }
2143  strcat(lFilterPatterns, "\n");
2144  if ( ! (aSingleFilterDescription && strlen(aSingleFilterDescription) ) )
2145  {
2146  strcpy(lDialogString, lFilterPatterns);
2147  strcat(lFilterPatterns, lDialogString);
2148  }
2149  strcat(lFilterPatterns, "All Files\n*.*\n");
2150  p = lFilterPatterns;
2151  while ((p = strchr(p, '\n')) != NULL)
2152  {
2153  *p = '\0';
2154  p ++ ;
2155  }
2156  }
2157 
2158  ofn.lStructSize = sizeof(OPENFILENAME) ;
2159  ofn.hwndOwner = GetForegroundWindow();
2160  ofn.hInstance = 0 ;
2161  ofn.lpstrFilter = lFilterPatterns && strlen(lFilterPatterns) ? lFilterPatterns : NULL;
2162  ofn.lpstrCustomFilter = NULL ;
2163  ofn.nMaxCustFilter = 0 ;
2164  ofn.nFilterIndex = 1 ;
2165  ofn.lpstrFile = aoBuff;
2166 
2167  ofn.nMaxFile = MAX_PATH_OR_CMD ;
2168  ofn.lpstrFileTitle = NULL ;
2169  ofn.nMaxFileTitle = MAX_PATH_OR_CMD / 2;
2170  ofn.lpstrInitialDir = lDirname && strlen(lDirname) ? lDirname : NULL;
2171  ofn.lpstrTitle = aTitle && strlen(aTitle) ? aTitle : NULL;
2172  ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR ;
2173  ofn.nFileOffset = 0 ;
2174  ofn.nFileExtension = 0 ;
2175  ofn.lpstrDefExt = ldefExt;
2176  ofn.lCustData = 0L ;
2177  ofn.lpfnHook = NULL ;
2178  ofn.lpTemplateName = NULL ;
2179 
2180  if ( GetSaveFileNameA ( & ofn ) == 0 )
2181  {
2182  lRetval = NULL ;
2183  }
2184  else
2185  {
2186  lRetval = aoBuff ;
2187  }
2188 
2189  if (lHResult==S_OK || lHResult==S_FALSE)
2190  {
2191  CoUninitialize();
2192  }
2193  return lRetval ;
2194 }
2195 
2196 
2197 static char const * openFileDialogWinGuiA(
2198  char * const aoBuff ,
2199  char const * const aTitle , /* NULL or "" */
2200  char const * const aDefaultPathAndFile , /* NULL or "" */
2201  int const aNumOfFilterPatterns , /* 0 */
2202  char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */
2203  char const * const aSingleFilterDescription , /* NULL or "image files" */
2204  int const aAllowMultipleSelects ) /* 0 or 1 */
2205 {
2206  char lDirname [MAX_PATH_OR_CMD] ;
2207  char lFilterPatterns[MAX_PATH_OR_CMD] = "";
2208  char lDialogString[MAX_PATH_OR_CMD] ;
2209  char * lPointers[MAX_MULTIPLE_FILES];
2210  size_t lLengths[MAX_MULTIPLE_FILES];
2211  int i , j ;
2212  char * p;
2213  size_t lBuffLen ;
2214  char * lRetval;
2215  HRESULT lHResult;
2216  OPENFILENAMEA ofn = {0};
2217 
2218  lHResult = CoInitializeEx(NULL,0);
2219 
2220  getPathWithoutFinalSlash(lDirname, aDefaultPathAndFile);
2221  getLastName(aoBuff, aDefaultPathAndFile);
2222 
2223  if (aNumOfFilterPatterns > 0)
2224  {
2225  if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )
2226  {
2227  strcpy(lFilterPatterns, aSingleFilterDescription);
2228  strcat(lFilterPatterns, "\n");
2229  }
2230  strcat(lFilterPatterns, aFilterPatterns[0]);
2231  for (i = 1; i < aNumOfFilterPatterns; i++)
2232  {
2233  strcat(lFilterPatterns, ";");
2234  strcat(lFilterPatterns, aFilterPatterns[i]);
2235  }
2236  strcat(lFilterPatterns, "\n");
2237  if ( ! (aSingleFilterDescription && strlen(aSingleFilterDescription) ) )
2238  {
2239  strcpy(lDialogString, lFilterPatterns);
2240  strcat(lFilterPatterns, lDialogString);
2241  }
2242  strcat(lFilterPatterns, "All Files\n*.*\n");
2243  p = lFilterPatterns;
2244  while ((p = strchr(p, '\n')) != NULL)
2245  {
2246  *p = '\0';
2247  p ++ ;
2248  }
2249  }
2250 
2251  ofn.lStructSize = sizeof( OPENFILENAME ) ;
2252  ofn.hwndOwner = GetForegroundWindow();
2253  ofn.hInstance = 0 ;
2254  ofn.lpstrFilter = lFilterPatterns && strlen(lFilterPatterns) ? lFilterPatterns : NULL;
2255  ofn.lpstrCustomFilter = NULL ;
2256  ofn.nMaxCustFilter = 0 ;
2257  ofn.nFilterIndex = 1 ;
2258  ofn.lpstrFile = aoBuff ;
2259  ofn.nMaxFile = MAX_PATH_OR_CMD ;
2260  ofn.lpstrFileTitle = NULL ;
2261  ofn.nMaxFileTitle = MAX_PATH_OR_CMD / 2;
2262  ofn.lpstrInitialDir = lDirname && strlen(lDirname) ? lDirname : NULL;
2263  ofn.lpstrTitle = aTitle && strlen(aTitle) ? aTitle : NULL;
2264  ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR ;
2265  ofn.nFileOffset = 0 ;
2266  ofn.nFileExtension = 0 ;
2267  ofn.lpstrDefExt = NULL ;
2268  ofn.lCustData = 0L ;
2269  ofn.lpfnHook = NULL ;
2270  ofn.lpTemplateName = NULL ;
2271 
2272  if ( aAllowMultipleSelects )
2273  {
2274  ofn.Flags |= OFN_ALLOWMULTISELECT;
2275  }
2276 
2277  if ( GetOpenFileNameA( & ofn ) == 0 )
2278  {
2279  lRetval = NULL ;
2280  }
2281  else
2282  {
2283  lBuffLen = strlen(aoBuff) ;
2284  lPointers[0] = aoBuff + lBuffLen + 1 ;
2285  if ( !aAllowMultipleSelects || (lPointers[0][0] == '\0') )
2286  {
2287  lRetval = aoBuff ;
2288  }
2289  else
2290  {
2291  i = 0 ;
2292  do
2293  {
2294  lLengths[i] = strlen(lPointers[i]);
2295  lPointers[i+1] = lPointers[i] + lLengths[i] + 1 ;
2296  i ++ ;
2297  }
2298  while ( lPointers[i][0] != '\0' );
2299  i--;
2300  p = aoBuff + MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD - 1 ;
2301  * p = '\0';
2302  for ( j = i ; j >=0 ; j-- )
2303  {
2304  p -= lLengths[j];
2305  memmove(p, lPointers[j], lLengths[j]);
2306  p--;
2307  *p = '\\';
2308  p -= lBuffLen ;
2309  memmove(p, aoBuff, lBuffLen);
2310  p--;
2311  *p = '|';
2312  }
2313  p++;
2314  lRetval = p ;
2315  }
2316  }
2317 
2318  if (lHResult==S_OK || lHResult==S_FALSE)
2319  {
2320  CoUninitialize();
2321  }
2322  return lRetval;
2323 }
2324 
2325 #ifndef TINYFD_NOSELECTFOLDERWIN
2326 static char const * selectFolderDialogWinGuiA(
2327  char * const aoBuff ,
2328  char const * const aTitle , /* NULL or "" */
2329  char const * const aDefaultPath ) /* NULL or "" */
2330 {
2331  BROWSEINFOA bInfo ;
2332  LPITEMIDLIST lpItem ;
2333  HRESULT lHResult ;
2334  char * lRetval = NULL ;
2335 
2336  lHResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
2337 
2338  /* we can't use aDefaultPath */
2339  bInfo.hwndOwner = GetForegroundWindow();
2340  bInfo.pidlRoot = NULL ;
2341  bInfo.pszDisplayName = aoBuff ;
2342  bInfo.lpszTitle = aTitle && strlen(aTitle) ? aTitle : NULL;
2343  if (lHResult == S_OK || lHResult == S_FALSE)
2344  {
2345  bInfo.ulFlags = BIF_USENEWUI;
2346  }
2347  bInfo.lpfn = BrowseCallbackProc;
2348  bInfo.lParam = (LPARAM)aDefaultPath;
2349  bInfo.iImage = -1 ;
2350 
2351  lpItem = SHBrowseForFolderA( & bInfo ) ;
2352  if ( lpItem )
2353  {
2354  SHGetPathFromIDListA( lpItem , aoBuff ) ;
2355  lRetval = aoBuff;
2356  }
2357 
2358  if (lHResult==S_OK || lHResult==S_FALSE)
2359  {
2360  CoUninitialize();
2361  }
2362  return lRetval;
2363 }
2364 #endif /*TINYFD_NOSELECTFOLDERWIN*/
2365 
2366 
2367 static char const * colorChooserWinGuiA(
2368  char const * const aTitle, /* NULL or "" */
2369  char const * const aDefaultHexRGB, /* NULL or "#FF0000"*/
2370  unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */
2371  unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */
2372 {
2373  static char lResultHexRGB[8];
2374 
2375  CHOOSECOLORA cc;
2376  COLORREF crCustColors[16];
2377  unsigned char lDefaultRGB[3];
2378  int lRet;
2379 
2380  if ( aDefaultHexRGB )
2381  {
2382  Hex2RGB(aDefaultHexRGB, lDefaultRGB);
2383  }
2384  else
2385  {
2386  lDefaultRGB[0]=aDefaultRGB[0];
2387  lDefaultRGB[1]=aDefaultRGB[1];
2388  lDefaultRGB[2]=aDefaultRGB[2];
2389  }
2390 
2391  /* we can't use aTitle */
2392  cc.lStructSize = sizeof( CHOOSECOLOR ) ;
2393  cc.hwndOwner = GetForegroundWindow();
2394  cc.hInstance = NULL ;
2395  cc.rgbResult = RGB(lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]);
2396  cc.lpCustColors = crCustColors;
2397  cc.Flags = CC_RGBINIT | CC_FULLOPEN;
2398  cc.lCustData = 0;
2399  cc.lpfnHook = NULL;
2400  cc.lpTemplateName = NULL;
2401 
2402  lRet = ChooseColorA(&cc);
2403 
2404  if ( ! lRet )
2405  {
2406  return NULL;
2407  }
2408 
2409  aoResultRGB[0] = GetRValue(cc.rgbResult);
2410  aoResultRGB[1] = GetGValue(cc.rgbResult);
2411  aoResultRGB[2] = GetBValue(cc.rgbResult);
2412 
2413  RGB2Hex(aoResultRGB, lResultHexRGB);
2414 
2415  return lResultHexRGB;
2416 }
2417 
2418 #endif /* TINYFD_NOLIB */
2419 
2420 static int dialogPresent(void)
2421 {
2422  static int lDialogPresent = -1 ;
2423  char lBuff [MAX_PATH_OR_CMD] ;
2424  FILE * lIn ;
2425  char const * lString = "dialog.exe";
2426  if ( lDialogPresent < 0 )
2427  {
2428  if (!(lIn = _popen("where dialog.exe","r")))
2429  {
2430  lDialogPresent = 0 ;
2431  return 0 ;
2432  }
2433  while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )
2434  {}
2435  _pclose( lIn ) ;
2436  if ( lBuff[strlen( lBuff ) -1] == '\n' )
2437  {
2438  lBuff[strlen( lBuff ) -1] = '\0' ;
2439  }
2440  if ( strcmp(lBuff+strlen(lBuff)-strlen(lString),lString) )
2441  {
2442  lDialogPresent = 0 ;
2443  }
2444  else
2445  {
2446  lDialogPresent = 1 ;
2447  }
2448  }
2449  return lDialogPresent;
2450 }
2451 
2452 
2453 static int messageBoxWinConsole(
2454  char const * const aTitle , /* NULL or "" */
2455  char const * const aMessage , /* NULL or "" may contain \n and \t */
2456  char const * const aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */
2457  char const * const aIconType , /* "info" "warning" "error" "question" */
2458  int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */
2459 {
2460  char lDialogString[MAX_PATH_OR_CMD];
2461  char lDialogFile[MAX_PATH_OR_CMD];
2462  FILE * lIn;
2463  char lBuff [MAX_PATH_OR_CMD] = "";
2464 
2465  strcpy( lDialogString , "dialog " ) ;
2466  if ( aTitle && strlen(aTitle) )
2467  {
2468  strcat(lDialogString, "--title \"") ;
2469  strcat(lDialogString, aTitle) ;
2470  strcat(lDialogString, "\" ") ;
2471  }
2472 
2473  if ( aDialogType && ( !strcmp( "okcancel" , aDialogType )
2474  || !strcmp("yesno", aDialogType) || !strcmp("yesnocancel", aDialogType) ) )
2475  {
2476  strcat(lDialogString, "--backtitle \"") ;
2477  strcat(lDialogString, "tab: move focus") ;
2478  strcat(lDialogString, "\" ") ;
2479  }
2480 
2481  if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) )
2482  {
2483  if ( ! aDefaultButton )
2484  {
2485  strcat( lDialogString , "--defaultno " ) ;
2486  }
2487  strcat( lDialogString ,
2488  "--yes-label \"Ok\" --no-label \"Cancel\" --yesno " ) ;
2489  }
2490  else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) )
2491  {
2492  if ( ! aDefaultButton )
2493  {
2494  strcat( lDialogString , "--defaultno " ) ;
2495  }
2496  strcat( lDialogString , "--yesno " ) ;
2497  }
2498  else if (aDialogType && !strcmp("yesnocancel", aDialogType))
2499  {
2500  if (!aDefaultButton)
2501  {
2502  strcat(lDialogString, "--defaultno ");
2503  }
2504  strcat(lDialogString, "--menu ");
2505  }
2506  else
2507  {
2508  strcat( lDialogString , "--msgbox " ) ;
2509  }
2510 
2511  strcat( lDialogString , "\"" ) ;
2512  if ( aMessage && strlen(aMessage) )
2513  {
2514  replaceSubStr( aMessage , "\n" , "\\n" , lBuff ) ;
2515  strcat(lDialogString, lBuff) ;
2516  lBuff[0]='\0';
2517  }
2518  strcat(lDialogString, "\" ");
2519 
2520  if (aDialogType && !strcmp("yesnocancel", aDialogType))
2521  {
2522  strcat(lDialogString, "0 60 0 Yes \"\" No \"\"");
2523  strcat(lDialogString, "2>>");
2524  }
2525  else
2526  {
2527  strcat(lDialogString, "10 60");
2528  strcat(lDialogString, " && echo 1 > ");
2529  }
2530 
2531  strcpy(lDialogFile, getenv("USERPROFILE"));
2532  strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt");
2533  strcat(lDialogString, lDialogFile);
2534 
2535  /*if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;*/
2536  system( lDialogString ) ;
2537 
2538  if (!(lIn = fopen(lDialogFile, "r")))
2539  {
2540  remove(lDialogFile);
2541  return 0 ;
2542  }
2543  while (fgets(lBuff, sizeof(lBuff), lIn) != NULL)
2544  {}
2545  fclose(lIn);
2546  remove(lDialogFile);
2547  if ( lBuff[strlen( lBuff ) -1] == '\n' )
2548  {
2549  lBuff[strlen( lBuff ) -1] = '\0' ;
2550  }
2551 
2552  /* if (tinyfd_verbose) printf("lBuff: %s\n", lBuff); */
2553  if ( ! strlen(lBuff) )
2554  {
2555  return 0;
2556  }
2557 
2558  if (aDialogType && !strcmp("yesnocancel", aDialogType))
2559  {
2560  if (lBuff[0] == 'Y') return 1;
2561  else return 2;
2562  }
2563 
2564  return 1;
2565 }
2566 
2567 
2568 static char const * inputBoxWinConsole(
2569  char * const aoBuff ,
2570  char const * const aTitle , /* NULL or "" */
2571  char const * const aMessage , /* NULL or "" may NOT contain \n nor \t */
2572  char const * const aDefaultInput ) /* "" , if NULL it's a passwordBox */
2573 {
2574  char lDialogString[MAX_PATH_OR_CMD];
2575  char lDialogFile[MAX_PATH_OR_CMD];
2576  FILE * lIn;
2577  int lResult;
2578 
2579  strcpy(lDialogFile, getenv("USERPROFILE"));
2580  strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt");
2581  strcpy(lDialogString , "echo|set /p=1 >" ) ;
2582  strcat(lDialogString, lDialogFile);
2583  strcat( lDialogString , " & " ) ;
2584 
2585  strcat( lDialogString , "dialog " ) ;
2586  if ( aTitle && strlen(aTitle) )
2587  {
2588  strcat(lDialogString, "--title \"") ;
2589  strcat(lDialogString, aTitle) ;
2590  strcat(lDialogString, "\" ") ;
2591  }
2592 
2593  strcat(lDialogString, "--backtitle \"") ;
2594  strcat(lDialogString, "tab: move focus") ;
2595  if ( ! aDefaultInput )
2596  {
2597  strcat(lDialogString, " (sometimes nothing, no blink nor star, is shown in text field)") ;
2598  }
2599 
2600  strcat(lDialogString, "\" ") ;
2601 
2602  if ( ! aDefaultInput )
2603  {
2604  strcat( lDialogString , "--insecure --passwordbox" ) ;
2605  }
2606  else
2607  {
2608  strcat( lDialogString , "--inputbox" ) ;
2609  }
2610  strcat( lDialogString , " \"" ) ;
2611  if ( aMessage && strlen(aMessage) )
2612  {
2613  strcat(lDialogString, aMessage) ;
2614  }
2615  strcat(lDialogString,"\" 10 60 ") ;
2616  if ( aDefaultInput && strlen(aDefaultInput) )
2617  {
2618  strcat(lDialogString, "\"") ;
2619  strcat(lDialogString, aDefaultInput) ;
2620  strcat(lDialogString, "\" ") ;
2621  }
2622 
2623  strcat(lDialogString, "2>>");
2624  strcpy(lDialogFile, getenv("USERPROFILE"));
2625  strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt");
2626  strcat(lDialogString, lDialogFile);
2627  strcat(lDialogString, " || echo 0 > ");
2628  strcat(lDialogString, lDialogFile);
2629 
2630  /* printf( "lDialogString: %s\n" , lDialogString ) ; */
2631  system( lDialogString ) ;
2632 
2633  if (!(lIn = fopen(lDialogFile, "r")))
2634  {
2635  remove(lDialogFile);
2636  return 0 ;
2637  }
2638  while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL)
2639  {}
2640  fclose(lIn);
2641 
2642  wipefile(lDialogFile);
2643  remove(lDialogFile);
2644  if ( aoBuff[strlen( aoBuff ) -1] == '\n' )
2645  {
2646  aoBuff[strlen( aoBuff ) -1] = '\0' ;
2647  }
2648  /* printf( "aoBuff: %s\n" , aoBuff ) ; */
2649 
2650  /* printf( "aoBuff: %s len: %lu \n" , aoBuff , strlen(aoBuff) ) ; */
2651  lResult = strncmp( aoBuff , "1" , 1) ? 0 : 1 ;
2652  /* printf( "lResult: %d \n" , lResult ) ; */
2653  if ( ! lResult )
2654  {
2655  return NULL ;
2656  }
2657  /* printf( "aoBuff+1: %s\n" , aoBuff+1 ) ; */
2658  return aoBuff+3 ;
2659 }
2660 
2661 
2662 static char const * saveFileDialogWinConsole(
2663  char * const aoBuff ,
2664  char const * const aTitle , /* NULL or "" */
2665  char const * const aDefaultPathAndFile ) /* NULL or "" */
2666 {
2667  char lDialogString[MAX_PATH_OR_CMD];
2668  char lPathAndFile[MAX_PATH_OR_CMD] = "";
2669  FILE * lIn;
2670 
2671  strcpy( lDialogString , "dialog " ) ;
2672  if ( aTitle && strlen(aTitle) )
2673  {
2674  strcat(lDialogString, "--title \"") ;
2675  strcat(lDialogString, aTitle) ;
2676  strcat(lDialogString, "\" ") ;
2677  }
2678 
2679  strcat(lDialogString, "--backtitle \"") ;
2680  strcat(lDialogString,
2681  "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ;
2682  strcat(lDialogString, "\" ") ;
2683 
2684  strcat( lDialogString , "--fselect \"" ) ;
2685  if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) )
2686  {
2687  /* dialog.exe uses unix separators even on windows */
2688  strcpy(lPathAndFile, aDefaultPathAndFile);
2689  replaceChr( lPathAndFile , '\\' , '/' ) ;
2690  }
2691 
2692  /* dialog.exe needs at least one separator */
2693  if ( ! strchr(lPathAndFile, '/') )
2694  {
2695  strcat(lDialogString, "./") ;
2696  }
2697  strcat(lDialogString, lPathAndFile) ;
2698  strcat(lDialogString, "\" 0 60 2>");
2699  strcpy(lPathAndFile, getenv("USERPROFILE"));
2700  strcat(lPathAndFile, "\\AppData\\Local\\Temp\\tinyfd.txt");
2701  strcat(lDialogString, lPathAndFile);
2702 
2703  /* printf( "lDialogString: %s\n" , lDialogString ) ; */
2704  system( lDialogString ) ;
2705 
2706  if (!(lIn = fopen(lPathAndFile, "r")))
2707  {
2708  remove(lPathAndFile);
2709  return NULL;
2710  }
2711  while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL)
2712  {}
2713  fclose(lIn);
2714  remove(lPathAndFile);
2715  replaceChr( aoBuff , '/' , '\\' ) ;
2716  /* printf( "aoBuff: %s\n" , aoBuff ) ; */
2717  getLastName(lDialogString,aoBuff);
2718  if ( ! strlen(lDialogString) )
2719  {
2720  return NULL;
2721  }
2722  return aoBuff;
2723 }
2724 
2725 
2726 static char const * openFileDialogWinConsole(
2727  char * const aoBuff ,
2728  char const * const aTitle , /* NULL or "" */
2729  char const * const aDefaultPathAndFile , /* NULL or "" */
2730  int const aAllowMultipleSelects ) /* 0 or 1 */
2731 {
2732  char lFilterPatterns[MAX_PATH_OR_CMD] = "";
2733  char lDialogString[MAX_PATH_OR_CMD] ;
2734  FILE * lIn;
2735 
2736  strcpy( lDialogString , "dialog " ) ;
2737  if ( aTitle && strlen(aTitle) )
2738  {
2739  strcat(lDialogString, "--title \"") ;
2740  strcat(lDialogString, aTitle) ;
2741  strcat(lDialogString, "\" ") ;
2742  }
2743 
2744  strcat(lDialogString, "--backtitle \"") ;
2745  strcat(lDialogString,
2746  "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ;
2747  strcat(lDialogString, "\" ") ;
2748 
2749  strcat( lDialogString , "--fselect \"" ) ;
2750  if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) )
2751  {
2752  /* dialog.exe uses unix separators even on windows */
2753  strcpy(lFilterPatterns, aDefaultPathAndFile);
2754  replaceChr( lFilterPatterns , '\\' , '/' ) ;
2755  }
2756 
2757  /* dialog.exe needs at least one separator */
2758  if ( ! strchr(lFilterPatterns, '/') )
2759  {
2760  strcat(lDialogString, "./") ;
2761  }
2762  strcat(lDialogString, lFilterPatterns) ;
2763  strcat(lDialogString, "\" 0 60 2>");
2764  strcpy(lFilterPatterns, getenv("USERPROFILE"));
2765  strcat(lFilterPatterns, "\\AppData\\Local\\Temp\\tinyfd.txt");
2766  strcat(lDialogString, lFilterPatterns);
2767 
2768  /* printf( "lDialogString: %s\n" , lDialogString ) ; */
2769  system( lDialogString ) ;
2770 
2771  if (!(lIn = fopen(lFilterPatterns, "r")))
2772  {
2773  remove(lFilterPatterns);
2774  return NULL;
2775  }
2776  while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL)
2777  {}
2778  fclose(lIn);
2779  remove(lFilterPatterns);
2780  replaceChr( aoBuff , '/' , '\\' ) ;
2781  /* printf( "aoBuff: %s\n" , aoBuff ) ; */
2782  return aoBuff;
2783 }
2784 
2785 
2786 static char const * selectFolderDialogWinConsole(
2787  char * const aoBuff ,
2788  char const * const aTitle , /* NULL or "" */
2789  char const * const aDefaultPath ) /* NULL or "" */
2790 {
2791  char lDialogString [MAX_PATH_OR_CMD] ;
2792  char lString [MAX_PATH_OR_CMD] ;
2793  FILE * lIn ;
2794 
2795  strcpy( lDialogString , "dialog " ) ;
2796  if ( aTitle && strlen(aTitle) )
2797  {
2798  strcat(lDialogString, "--title \"") ;
2799  strcat(lDialogString, aTitle) ;
2800  strcat(lDialogString, "\" ") ;
2801  }
2802 
2803  strcat(lDialogString, "--backtitle \"") ;
2804  strcat(lDialogString,
2805  "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ;
2806  strcat(lDialogString, "\" ") ;
2807 
2808  strcat( lDialogString , "--dselect \"" ) ;
2809  if ( aDefaultPath && strlen(aDefaultPath) )
2810  {
2811  /* dialog.exe uses unix separators even on windows */
2812  strcpy(lString, aDefaultPath) ;
2813  ensureFinalSlash(lString);
2814  replaceChr( lString , '\\' , '/' ) ;
2815  strcat(lDialogString, lString) ;
2816  }
2817  else
2818  {
2819  /* dialog.exe needs at least one separator */
2820  strcat(lDialogString, "./") ;
2821  }
2822  strcat(lDialogString, "\" 0 60 2>");
2823  strcpy(lString, getenv("USERPROFILE"));
2824  strcat(lString, "\\AppData\\Local\\Temp\\tinyfd.txt");
2825  strcat(lDialogString, lString);
2826 
2827  /* printf( "lDialogString: %s\n" , lDialogString ) ; */
2828  system( lDialogString ) ;
2829 
2830  if (!(lIn = fopen(lString, "r")))
2831  {
2832  remove(lString);
2833  return NULL;
2834  }
2835  while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL)
2836  {}
2837  fclose(lIn);
2838  remove(lString);
2839  replaceChr( aoBuff , '/' , '\\' ) ;
2840  /* printf( "aoBuff: %s\n" , aoBuff ) ; */
2841  return aoBuff;
2842 }
2843 
2844 
2845 int tinyfd_messageBox(
2846  char const * const aTitle , /* NULL or "" */
2847  char const * const aMessage , /* NULL or "" may contain \n and \t */
2848  char const * const aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */
2849  char const * const aIconType , /* "info" "warning" "error" "question" */
2850  int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */
2851 {
2852  char lChar ;
2853 
2854 #ifndef TINYFD_NOLIB
2855  if ((!tinyfd_forceConsole || !(GetConsoleWindow() || dialogPresent()))
2856  && (!getenv("SSH_CLIENT") || getenv("DISPLAY")))
2857  {
2858  if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "windows"); return 1; }
2859  if (tinyfd_winUtf8)
2860  {
2861  return messageBoxWinGui8(
2862  aTitle, aMessage, aDialogType, aIconType, aDefaultButton);
2863  }
2864  else
2865  {
2866  return messageBoxWinGuiA(
2867  aTitle, aMessage, aDialogType, aIconType, aDefaultButton);
2868  }
2869  }
2870  else
2871 #endif /* TINYFD_NOLIB */
2872  if ( dialogPresent() )
2873  {
2874  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return 0;}
2875  return messageBoxWinConsole(
2876  aTitle,aMessage,aDialogType,aIconType,aDefaultButton);
2877  }
2878  else
2879  {
2880  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return 0;}
2882  {
2883  gWarningDisplayed = 1;
2884  printf("\n\n%s\n", gTitle);
2885  printf("%s\n\n", tinyfd_needs);
2886  }
2887  if ( aTitle && strlen(aTitle) )
2888  {
2889  printf("\n%s\n\n", aTitle);
2890  }
2891  if ( aDialogType && !strcmp("yesno",aDialogType) )
2892  {
2893  do
2894  {
2895  if ( aMessage && strlen(aMessage) )
2896  {
2897  printf("%s\n",aMessage);
2898  }
2899  printf("y/n: ");
2900  lChar = (char) tolower( _getch() ) ;
2901  printf("\n\n");
2902  }
2903  while ( lChar != 'y' && lChar != 'n' ) ;
2904  return lChar == 'y' ? 1 : 0 ;
2905  }
2906  else if ( aDialogType && !strcmp("okcancel",aDialogType) )
2907  {
2908  do
2909  {
2910  if ( aMessage && strlen(aMessage) )
2911  {
2912  printf("%s\n",aMessage);
2913  }
2914  printf("[O]kay/[C]ancel: ");
2915  lChar = (char) tolower( _getch() ) ;
2916  printf("\n\n");
2917  }
2918  while ( lChar != 'o' && lChar != 'c' ) ;
2919  return lChar == 'o' ? 1 : 0 ;
2920  }
2921  else if (aDialogType && !strcmp("yesnocancel", aDialogType))
2922  {
2923  do
2924  {
2925  if (aMessage && strlen(aMessage))
2926  {
2927  printf("%s\n", aMessage);
2928  }
2929  printf("[Y]es/[N]o/[C]ancel: ");
2930  lChar = (char)tolower(_getch());
2931  printf("\n\n");
2932  } while (lChar != 'y' && lChar != 'n' && lChar != 'c');
2933  return (lChar == 'y') ? 1 : (lChar == 'n') ? 2 : 0 ;
2934  }
2935  else
2936  {
2937  if ( aMessage && strlen(aMessage) )
2938  {
2939  printf("%s\n\n",aMessage);
2940  }
2941  printf("press enter to continue ");
2942  lChar = (char) _getch() ;
2943  printf("\n\n");
2944  return 1 ;
2945  }
2946  }
2947 }
2948 
2949 
2950 /* return has only meaning for tinyfd_query */
2951 int tinyfd_notifyPopup(
2952  char const * const aTitle , /* NULL or "" */
2953  char const * const aMessage , /* NULL or "" may contain \n \t */
2954  char const * const aIconType ) /* "info" "warning" "error" */
2955 {
2956 #ifndef TINYFD_NOLIB
2957  if ((!tinyfd_forceConsole || !(
2958  GetConsoleWindow() ||
2959  dialogPresent()))
2960  && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) )
2961  {
2962  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return 1;}
2963  return notifyWinGui(aTitle, aMessage, aIconType);
2964  }
2965  else
2966 #endif /* TINYFD_NOLIB */
2967  {
2968  return tinyfd_messageBox(aTitle, aMessage, "ok" , aIconType, 0);
2969  }
2970 }
2971 
2972 
2973 /* returns NULL on cancel */
2974 char const * tinyfd_inputBox(
2975  char const * const aTitle , /* NULL or "" */
2976  char const * const aMessage , /* NULL or "" may NOT contain \n nor \t */
2977  char const * const aDefaultInput ) /* "" , if NULL it's a passwordBox */
2978 {
2979  static char lBuff [MAX_PATH_OR_CMD] ;
2980  char * lEOF;
2981 
2982 #ifndef TINYFD_NOLIB
2983  DWORD mode = 0;
2984  HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
2985 
2986  if ((!tinyfd_forceConsole || !(
2987  GetConsoleWindow() ||
2988  dialogPresent()))
2989  && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) )
2990  {
2991  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;}
2992  lBuff[0]='\0';
2993  return inputBoxWinGui(lBuff, aTitle, aMessage, aDefaultInput);
2994  }
2995  else
2996 #endif /* TINYFD_NOLIB */
2997  if ( dialogPresent() )
2998  {
2999  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;}
3000  lBuff[0]='\0';
3001  return inputBoxWinConsole(lBuff,aTitle,aMessage,aDefaultInput);
3002  }
3003  else
3004  {
3005  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;}
3006  lBuff[0]='\0';
3008  {
3009  gWarningDisplayed = 1 ;
3010  printf("\n\n%s\n", gTitle);
3011  printf("%s\n\n", tinyfd_needs);
3012  }
3013  if ( aTitle && strlen(aTitle) )
3014  {
3015  printf("\n%s\n\n", aTitle);
3016  }
3017  if ( aMessage && strlen(aMessage) )
3018  {
3019  printf("%s\n",aMessage);
3020  }
3021  printf("(ctrl-Z + enter to cancel): ");
3022 #ifndef TINYFD_NOLIB
3023  if ( ! aDefaultInput )
3024  {
3025  GetConsoleMode(hStdin,&mode);
3026  SetConsoleMode(hStdin,mode & (~ENABLE_ECHO_INPUT) );
3027  }
3028 #endif /* TINYFD_NOLIB */
3029  lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin);
3030  if ( ! lEOF )
3031  {
3032  return NULL;
3033  }
3034 #ifndef TINYFD_NOLIB
3035  if ( ! aDefaultInput )
3036  {
3037  SetConsoleMode(hStdin,mode);
3038  printf("\n");
3039  }
3040 #endif /* TINYFD_NOLIB */
3041  printf("\n");
3042  if ( strchr(lBuff,27) )
3043  {
3044  return NULL ;
3045  }
3046  if ( lBuff[strlen( lBuff ) -1] == '\n' )
3047  {
3048  lBuff[strlen( lBuff ) -1] = '\0' ;
3049  }
3050  return lBuff ;
3051  }
3052 }
3053 
3054 
3055 char const * tinyfd_saveFileDialog(
3056  char const * const aTitle , /* NULL or "" */
3057  char const * const aDefaultPathAndFile , /* NULL or "" */
3058  int const aNumOfFilterPatterns , /* 0 */
3059  char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */
3060  char const * const aSingleFilterDescription ) /* NULL or "image files" */
3061 {
3062  static char lBuff [MAX_PATH_OR_CMD] ;
3063  char lString[MAX_PATH_OR_CMD] ;
3064  char const * p ;
3065  lBuff[0]='\0';
3066 #ifndef TINYFD_NOLIB
3067  if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) )
3068  && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) )
3069  {
3070  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;}
3071  if (tinyfd_winUtf8)
3072  {
3073  p = saveFileDialogWinGui8(lBuff,
3074  aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription);
3075  }
3076  else
3077  {
3078  p = saveFileDialogWinGuiA(lBuff,
3079  aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription);
3080  }
3081  }
3082  else
3083 #endif /* TINYFD_NOLIB */
3084  if ( dialogPresent() )
3085  {
3086  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;}
3087  p = saveFileDialogWinConsole(lBuff,aTitle,aDefaultPathAndFile);
3088  }
3089  else
3090  {
3091  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;}
3092  p = tinyfd_inputBox(aTitle, "Save file","");
3093  }
3094 
3095  if ( ! p || ! strlen( p ) )
3096  {
3097  return NULL;
3098  }
3099  getPathWithoutFinalSlash( lString , p ) ;
3100  if ( strlen( lString ) && ! dirExists( lString ) )
3101  {
3102  return NULL ;
3103  }
3104  getLastName(lString,p);
3105  if ( ! filenameValid(lString) )
3106  {
3107  return NULL;
3108  }
3109  return p ;
3110 }
3111 
3112 
3113 /* in case of multiple files, the separator is | */
3114 char const * tinyfd_openFileDialog(
3115  char const * const aTitle , /* NULL or "" */
3116  char const * const aDefaultPathAndFile , /* NULL or "" */
3117  int const aNumOfFilterPatterns , /* 0 */
3118  char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */
3119  char const * const aSingleFilterDescription , /* NULL or "image files" */
3120  int const aAllowMultipleSelects ) /* 0 or 1 */
3121 {
3122  static char lBuff[MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD];
3123  char const * p ;
3124 #ifndef TINYFD_NOLIB
3125  if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) )
3126  && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) )
3127  {
3128  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;}
3129  if (tinyfd_winUtf8)
3130  {
3131  p = openFileDialogWinGui8(lBuff,
3132  aTitle, aDefaultPathAndFile, aNumOfFilterPatterns,
3133  aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects);
3134  }
3135  else
3136  {
3137  p = openFileDialogWinGuiA(lBuff,
3138  aTitle, aDefaultPathAndFile, aNumOfFilterPatterns,
3139  aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects);
3140  }
3141  }
3142  else
3143 #endif /* TINYFD_NOLIB */
3144  if ( dialogPresent() )
3145  {
3146  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;}
3147  p = openFileDialogWinConsole(lBuff,
3148  aTitle,aDefaultPathAndFile,aAllowMultipleSelects);
3149  }
3150  else
3151  {
3152  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;}
3153  p = tinyfd_inputBox(aTitle, "Open file","");
3154  }
3155 
3156  if ( ! p || ! strlen( p ) )
3157  {
3158  return NULL;
3159  }
3160  if ( aAllowMultipleSelects && strchr(p, '|') )
3161  {
3162  p = ensureFilesExist( lBuff , p ) ;
3163  }
3164  else if ( ! fileExists(p) )
3165  {
3166  return NULL ;
3167  }
3168  /* printf( "lBuff3: %s\n" , p ) ; */
3169  return p ;
3170 }
3171 
3172 
3173 char const * tinyfd_selectFolderDialog(
3174  char const * const aTitle , /* NULL or "" */
3175  char const * const aDefaultPath ) /* NULL or "" */
3176 {
3177  static char lBuff [MAX_PATH_OR_CMD] ;
3178  char const * p ;
3179 #ifndef TINYFD_NOLIB
3180  if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) )
3181  && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) )
3182  {
3183  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;}
3184  if (tinyfd_winUtf8)
3185  {
3186 #ifndef TINYFD_NOSELECTFOLDERWIN
3187  p = selectFolderDialogWinGui8(lBuff, aTitle, aDefaultPath);
3188  }
3189  else
3190  {
3191  p = selectFolderDialogWinGuiA(lBuff, aTitle, aDefaultPath);
3192 #endif /*TINYFD_NOSELECTFOLDERWIN*/
3193  }
3194  }
3195  else
3196 #endif /* TINYFD_NOLIB */
3197  if ( dialogPresent() )
3198  {
3199  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;}
3200  p = selectFolderDialogWinConsole(lBuff,aTitle,aDefaultPath);
3201  }
3202  else
3203  {
3204  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;}
3205  p = tinyfd_inputBox(aTitle, "Select folder","");
3206  }
3207 
3208  if ( ! p || ! strlen( p ) || ! dirExists( p ) )
3209  {
3210  return NULL ;
3211  }
3212  return p ;
3213 }
3214 
3215 
3216 /* returns the hexcolor as a string "#FF0000" */
3217 /* aoResultRGB also contains the result */
3218 /* aDefaultRGB is used only if aDefaultHexRGB is NULL */
3219 /* aDefaultRGB and aoResultRGB can be the same array */
3220 char const * tinyfd_colorChooser(
3221  char const * const aTitle, /* NULL or "" */
3222  char const * const aDefaultHexRGB, /* NULL or "#FF0000"*/
3223  unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */
3224  unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */
3225 {
3226  char lDefaultHexRGB[8];
3227  char * lpDefaultHexRGB;
3228  int i;
3229  char const * p ;
3230 
3231 #ifndef TINYFD_NOLIB
3232  if ( (!tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent()) )
3233  && (!getenv("SSH_CLIENT") || getenv("DISPLAY")) )
3234  {
3235  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;}
3236  if (tinyfd_winUtf8)
3237  {
3238  return colorChooserWinGui8(
3239  aTitle, aDefaultHexRGB, aDefaultRGB, aoResultRGB);
3240  }
3241  else
3242  {
3243  return colorChooserWinGuiA(
3244  aTitle, aDefaultHexRGB, aDefaultRGB, aoResultRGB);
3245  }
3246  }
3247  else
3248 #endif /* TINYFD_NOLIB */
3249  if ( aDefaultHexRGB )
3250  {
3251  lpDefaultHexRGB = (char *) aDefaultHexRGB ;
3252  }
3253  else
3254  {
3255  RGB2Hex( aDefaultRGB , lDefaultHexRGB ) ;
3256  lpDefaultHexRGB = (char *) lDefaultHexRGB ;
3257  }
3258  p = tinyfd_inputBox(aTitle,
3259  "Enter hex rgb color (i.e. #f5ca20)",lpDefaultHexRGB);
3260  if (aTitle&&!strcmp(aTitle,"tinyfd_query")) return p;
3261 
3262  if ( !p || (strlen(p) != 7) || (p[0] != '#') )
3263  {
3264  return NULL ;
3265  }
3266  for ( i = 1 ; i < 7 ; i ++ )
3267  {
3268  if ( ! isxdigit( p[i] ) )
3269  {
3270  return NULL ;
3271  }
3272  }
3273  Hex2RGB(p,aoResultRGB);
3274  return p ;
3275 }
3276 
3277 
3278 #else /* unix */
3279 
3280 static char gPython2Name[16];
3281 static char gPython3Name[16];
3282 static char gPythonName[16];
3283 
3284 static int isDarwin(void)
3285 {
3286  static int lsIsDarwin = -1 ;
3287  struct utsname lUtsname ;
3288  if ( lsIsDarwin < 0 )
3289  {
3290  lsIsDarwin = !uname(&lUtsname) && !strcmp(lUtsname.sysname,"Darwin") ;
3291  }
3292  return lsIsDarwin ;
3293 }
3294 
3295 
3296 static int dirExists( char const * const aDirPath )
3297 {
3298  DIR * lDir ;
3299  if ( ! aDirPath || ! strlen( aDirPath ) )
3300  return 0 ;
3301  lDir = opendir( aDirPath ) ;
3302  if ( ! lDir )
3303  {
3304  return 0 ;
3305  }
3306  closedir( lDir ) ;
3307  return 1 ;
3308 }
3309 
3310 
3311 static int detectPresence( char const * const aExecutable )
3312 {
3313  char lBuff [MAX_PATH_OR_CMD] ;
3314  char lTestedString [MAX_PATH_OR_CMD] = "which " ;
3315  FILE * lIn ;
3316 
3317  strcat( lTestedString , aExecutable ) ;
3318  strcat( lTestedString, " 2>/dev/null ");
3319  lIn = popen( lTestedString , "r" ) ;
3320  if ( ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )
3321  && ( ! strchr( lBuff , ':' ) )
3322  && ( strncmp(lBuff, "no ", 3) ) )
3323  { /* present */
3324  pclose( lIn ) ;
3325  if (tinyfd_verbose) printf("detectPresence %s %d\n", aExecutable, 1);
3326  return 1 ;
3327  }
3328  else
3329  {
3330  pclose( lIn ) ;
3331  if (tinyfd_verbose) printf("detectPresence %s %d\n", aExecutable, 0);
3332  return 0 ;
3333  }
3334 }
3335 
3336 
3337 static char const * getVersion( char const * const aExecutable ) /*version must be first numeral*/
3338 {
3339  static char lBuff [MAX_PATH_OR_CMD] ;
3340  char lTestedString [MAX_PATH_OR_CMD] ;
3341  FILE * lIn ;
3342  char * lTmp ;
3343 
3344  strcpy( lTestedString , aExecutable ) ;
3345  strcat( lTestedString , " --version" ) ;
3346 
3347  lIn = popen( lTestedString , "r" ) ;
3348  lTmp = fgets( lBuff , sizeof( lBuff ) , lIn ) ;
3349  pclose( lIn ) ;
3350 
3351  lTmp += strcspn(lTmp,"0123456789");
3352  /* printf("lTmp:%s\n", lTmp); */
3353  return lTmp ;
3354 }
3355 
3356 
3357 static int * const getMajorMinorPatch( char const * const aExecutable )
3358 {
3359  static int lArray [3] ;
3360  char * lTmp ;
3361 
3362  lTmp = (char *) getVersion(aExecutable);
3363  lArray[0] = atoi( strtok(lTmp," ,.-") ) ;
3364  /* printf("lArray0 %d\n", lArray[0]); */
3365  lArray[1] = atoi( strtok(0," ,.-") ) ;
3366  /* printf("lArray1 %d\n", lArray[1]); */
3367  lArray[2] = atoi( strtok(0," ,.-") ) ;
3368  /* printf("lArray2 %d\n", lArray[2]); */
3369 
3370  if ( !lArray[0] && !lArray[1] && !lArray[2] ) return NULL;
3371  return lArray ;
3372 }
3373 
3374 
3375 static int tryCommand( char const * const aCommand )
3376 {
3377  char lBuff [MAX_PATH_OR_CMD] ;
3378  FILE * lIn ;
3379 
3380  lIn = popen( aCommand , "r" ) ;
3381  if ( fgets( lBuff , sizeof( lBuff ) , lIn ) == NULL )
3382  { /* present */
3383  pclose( lIn ) ;
3384  return 1 ;
3385  }
3386  else
3387  {
3388  pclose( lIn ) ;
3389  return 0 ;
3390  }
3391 
3392 }
3393 
3394 
3395 static int isTerminalRunning(void)
3396 {
3397  static int lIsTerminalRunning = -1 ;
3398  if ( lIsTerminalRunning < 0 )
3399  {
3400  lIsTerminalRunning = isatty(1);
3401  if (tinyfd_verbose) printf("isTerminalRunning %d\n", lIsTerminalRunning );
3402  }
3403  return lIsTerminalRunning;
3404 }
3405 
3406 
3407 static char const * dialogNameOnly(void)
3408 {
3409  static char lDialogName[128] = "*" ;
3410  if ( lDialogName[0] == '*' )
3411  {
3412  if ( isDarwin() && * strcpy(lDialogName , "/opt/local/bin/dialog" )
3413  && detectPresence( lDialogName ) )
3414  {}
3415  else if ( * strcpy(lDialogName , "dialog" )
3416  && detectPresence( lDialogName ) )
3417  {}
3418  else
3419  {
3420  strcpy(lDialogName , "" );
3421  }
3422  }
3423  return lDialogName ;
3424 }
3425 
3426 
3428 {
3429  char const * lDialogName ;
3430  char * lVersion ;
3431  int lMajor ;
3432  int lMinor ;
3433  int lDate ;
3434  int lResult ;
3435  char * lMinorP ;
3436  char * lLetter ;
3437  char lBuff[128] ;
3438 
3439  /*char lTest[128] = " 0.9b-20031126" ;*/
3440 
3441  lDialogName = dialogNameOnly() ;
3442  if ( ! strlen(lDialogName) || !(lVersion = (char *) getVersion(lDialogName)) ) return 0 ;
3443  /*lVersion = lTest ;*/
3444  /*printf("lVersion %s\n", lVersion);*/
3445  strcpy(lBuff,lVersion);
3446  lMajor = atoi( strtok(lVersion," ,.-") ) ;
3447  /*printf("lMajor %d\n", lMajor);*/
3448  lMinorP = strtok(0," ,.-abcdefghijklmnopqrstuvxyz");
3449  lMinor = atoi( lMinorP ) ;
3450  /*printf("lMinor %d\n", lMinor );*/
3451  lDate = atoi( strtok(0," ,.-") ) ;
3452  if (lDate<0) lDate = - lDate;
3453  /*printf("lDate %d\n", lDate);*/
3454  lLetter = lMinorP + strlen(lMinorP) ;
3455  strcpy(lVersion,lBuff);
3456  strtok(lLetter," ,.-");
3457  /*printf("lLetter %s\n", lLetter);*/
3458  lResult = (lMajor > 0) || ( ( lMinor == 9 ) && (*lLetter == 'b') && (lDate >= 20031126) );
3459  /*printf("lResult %d\n", lResult);*/
3460  return lResult;
3461 }
3462 
3463 
3464 static int whiptailPresentOnly(void)
3465 {
3466  static int lWhiptailPresent = -1 ;
3467  if ( lWhiptailPresent < 0 )
3468  {
3469  lWhiptailPresent = detectPresence( "whiptail" ) ;
3470  }
3471  return lWhiptailPresent ;
3472 }
3473 
3474 
3475 static char const * terminalName(void)
3476 {
3477  static char lTerminalName[128] = "*" ;
3478  char lShellName[64] = "*" ;
3479  int * lArray;
3480 
3481  if ( lTerminalName[0] == '*' )
3482  {
3483  if ( detectPresence( "bash" ) )
3484  {
3485  strcpy(lShellName , "bash -c " ) ; /*good for basic input*/
3486  }
3487  else if ( strlen(dialogNameOnly()) || whiptailPresentOnly() )
3488  {
3489  strcpy(lShellName , "sh -c " ) ; /*good enough for dialog & whiptail*/
3490  }
3491  else
3492  {
3493  strcpy(lTerminalName , "" ) ;
3494  return NULL ;
3495  }
3496 
3497  if ( isDarwin() )
3498  {
3499  if ( * strcpy(lTerminalName , "/opt/X11/bin/xterm" )
3500  && detectPresence( lTerminalName ) )
3501  {
3502  strcat(lTerminalName , " -fa 'DejaVu Sans Mono' -fs 10 -title tinyfiledialogs -e " ) ;
3503  strcat(lTerminalName , lShellName ) ;
3504  }
3505  else
3506  {
3507  strcpy(lTerminalName , "" ) ;
3508  }
3509  }
3510  else if ( * strcpy(lTerminalName,"xterm") /*good (small without parameters)*/
3511  && detectPresence(lTerminalName) )
3512  {
3513  strcat(lTerminalName , " -fa 'DejaVu Sans Mono' -fs 10 -title tinyfiledialogs -e " ) ;
3514  strcat(lTerminalName , lShellName ) ;
3515  }
3516  else if ( * strcpy(lTerminalName,"terminator") /*good*/
3517  && detectPresence(lTerminalName) )
3518  {
3519  strcat(lTerminalName , " -x " ) ;
3520  strcat(lTerminalName , lShellName ) ;
3521  }
3522  else if ( * strcpy(lTerminalName,"lxterminal") /*good*/
3523  && detectPresence(lTerminalName) )
3524  {
3525  strcat(lTerminalName , " -e " ) ;
3526  strcat(lTerminalName , lShellName ) ;
3527  }
3528  else if ( * strcpy(lTerminalName,"konsole") /*good*/
3529  && detectPresence(lTerminalName) )
3530  {
3531  strcat(lTerminalName , " -e " ) ;
3532  strcat(lTerminalName , lShellName ) ;
3533  }
3534  else if ( * strcpy(lTerminalName,"kterm") /*good*/
3535  && detectPresence(lTerminalName) )
3536  {
3537  strcat(lTerminalName , " -e " ) ;
3538  strcat(lTerminalName , lShellName ) ;
3539  }
3540  else if ( * strcpy(lTerminalName,"tilix") /*good*/
3541  && detectPresence(lTerminalName) )
3542  {
3543  strcat(lTerminalName , " -e " ) ;
3544  strcat(lTerminalName , lShellName ) ;
3545  }
3546  else if ( * strcpy(lTerminalName,"xfce4-terminal") /*good*/
3547  && detectPresence(lTerminalName) )
3548  {
3549  strcat(lTerminalName , " -x " ) ;
3550  strcat(lTerminalName , lShellName ) ;
3551  }
3552  else if ( * strcpy(lTerminalName,"mate-terminal") /*good*/
3553  && detectPresence(lTerminalName) )
3554  {
3555  strcat(lTerminalName , " -x " ) ;
3556  strcat(lTerminalName , lShellName ) ;
3557  }
3558  else if ( * strcpy(lTerminalName,"Eterm") /*good*/
3559  && detectPresence(lTerminalName) )
3560  {
3561  strcat(lTerminalName , " -e " ) ;
3562  strcat(lTerminalName , lShellName ) ;
3563  }
3564  else if ( * strcpy(lTerminalName,"evilvte") /*good*/
3565  && detectPresence(lTerminalName) )
3566  {
3567  strcat(lTerminalName , " -e " ) ;
3568  strcat(lTerminalName , lShellName ) ;
3569  }
3570  else if ( * strcpy(lTerminalName,"pterm") /*good (only letters)*/
3571  && detectPresence(lTerminalName) )
3572  {
3573  strcat(lTerminalName , " -e " ) ;
3574  strcat(lTerminalName , lShellName ) ;
3575  }
3576  else if ( * strcpy(lTerminalName,"gnome-terminal")
3577  && detectPresence(lTerminalName) && (lArray = getMajorMinorPatch(lTerminalName))
3578  && ((lArray[0]<3) || (lArray[0]==3 && lArray[1]<=6)) )
3579  {
3580  strcat(lTerminalName , " --disable-factory -x " ) ;
3581  strcat(lTerminalName , lShellName ) ;
3582  }
3583  else
3584  {
3585  strcpy(lTerminalName , "" ) ;
3586  }
3587  /* bad: koi rxterm guake tilda vala-terminal qterminal
3588  aterm Terminal terminology sakura lilyterm weston-terminal
3589  roxterm termit xvt rxvt mrxvt urxvt */
3590  }
3591  if ( strlen(lTerminalName) )
3592  {
3593  return lTerminalName ;
3594  }
3595  else
3596  {
3597  return NULL ;
3598  }
3599 }
3600 
3601 
3602 static char const * dialogName(void)
3603 {
3604  char const * lDialogName ;
3605  lDialogName = dialogNameOnly( ) ;
3606  if ( strlen(lDialogName) && ( isTerminalRunning() || terminalName() ) )
3607  {
3608  return lDialogName ;
3609  }
3610  else
3611  {
3612  return NULL ;
3613  }
3614 }
3615 
3616 
3617 static int whiptailPresent(void)
3618 {
3619  int lWhiptailPresent ;
3620  lWhiptailPresent = whiptailPresentOnly( ) ;
3621  if ( lWhiptailPresent && ( isTerminalRunning() || terminalName() ) )
3622  {
3623  return lWhiptailPresent ;
3624  }
3625  else
3626  {
3627  return 0 ;
3628  }
3629 }
3630 
3631 
3632 
3633 static int graphicMode(void)
3634 {
3635  return !( tinyfd_forceConsole && (isTerminalRunning() || terminalName()) )
3636  && ( getenv("DISPLAY")
3637  || (isDarwin() && (!getenv("SSH_TTY") || getenv("DISPLAY") ) ) ) ;
3638 }
3639 
3640 
3641 static int pactlPresent(void)
3642 {
3643  static int lPactlPresent = -1 ;
3644  if ( lPactlPresent < 0 )
3645  {
3646  lPactlPresent = detectPresence("pactl") ;
3647  }
3648  return lPactlPresent ;
3649 }
3650 
3651 
3652 static int speakertestPresent(void)
3653 {
3654  static int lSpeakertestPresent = -1 ;
3655  if ( lSpeakertestPresent < 0 )
3656  {
3657  lSpeakertestPresent = detectPresence("speaker-test") ;
3658  }
3659  return lSpeakertestPresent ;
3660 }
3661 
3662 
3663 static int beepexePresent(void)
3664 {
3665  static int lBeepexePresent = -1 ;
3666  if ( lBeepexePresent < 0 )
3667  {
3668  lBeepexePresent = detectPresence("beep.exe") ;
3669  }
3670  return lBeepexePresent ;
3671 }
3672 
3673 
3674 static int xmessagePresent(void)
3675 {
3676  static int lXmessagePresent = -1 ;
3677  if ( lXmessagePresent < 0 )
3678  {
3679  lXmessagePresent = detectPresence("xmessage");/*if not tty,not on osxpath*/
3680  }
3681  return lXmessagePresent && graphicMode( ) ;
3682 }
3683 
3684 
3685 static int gxmessagePresent(void)
3686 {
3687  static int lGxmessagePresent = -1 ;
3688  if ( lGxmessagePresent < 0 )
3689  {
3690  lGxmessagePresent = detectPresence("gxmessage") ;
3691  }
3692  return lGxmessagePresent && graphicMode( ) ;
3693 }
3694 
3695 
3696 static int gmessagePresent(void)
3697 {
3698  static int lGmessagePresent = -1 ;
3699  if ( lGmessagePresent < 0 )
3700  {
3701  lGmessagePresent = detectPresence("gmessage") ;
3702  }
3703  return lGmessagePresent && graphicMode( ) ;
3704 }
3705 
3706 
3707 static int notifysendPresent(void)
3708 {
3709  static int lNotifysendPresent = -1 ;
3710  if ( lNotifysendPresent < 0 )
3711  {
3712  lNotifysendPresent = detectPresence("notify-send") ;
3713  }
3714  return lNotifysendPresent && graphicMode( ) ;
3715 }
3716 
3717 
3718 static int perlPresent(void)
3719 {
3720  static int lPerlPresent = -1 ;
3721  char lBuff [MAX_PATH_OR_CMD] ;
3722  FILE * lIn ;
3723 
3724  if ( lPerlPresent < 0 )
3725  {
3726  lPerlPresent = detectPresence("perl") ;
3727  if ( lPerlPresent )
3728  {
3729  lIn = popen( "perl -MNet::DBus -e \"Net::DBus->session->get_service('org.freedesktop.Notifications')\" 2>&1" , "r" ) ;
3730  if ( fgets( lBuff , sizeof( lBuff ) , lIn ) == NULL )
3731  {
3732  lPerlPresent = 2 ;
3733  }
3734  pclose( lIn ) ;
3735  if (tinyfd_verbose) printf("perl-dbus %d\n", lPerlPresent);
3736  }
3737  }
3738  return graphicMode() ? lPerlPresent : 0 ;
3739 }
3740 
3741 
3742 static int afplayPresent(void)
3743 {
3744  static int lAfplayPresent = -1 ;
3745  char lBuff [MAX_PATH_OR_CMD] ;
3746  FILE * lIn ;
3747 
3748  if ( lAfplayPresent < 0 )
3749  {
3750  lAfplayPresent = detectPresence("afplay") ;
3751  if ( lAfplayPresent )
3752  {
3753  lIn = popen( "test -e /System/Library/Sounds/Ping.aiff || echo Ping" , "r" ) ;
3754  if ( fgets( lBuff , sizeof( lBuff ) , lIn ) == NULL )
3755  {
3756  lAfplayPresent = 2 ;
3757  }
3758  pclose( lIn ) ;
3759  if (tinyfd_verbose) printf("afplay %d\n", lAfplayPresent);
3760  }
3761  }
3762  return graphicMode() ? lAfplayPresent : 0 ;
3763 }
3764 
3765 
3766 static int xdialogPresent(void)
3767 {
3768  static int lXdialogPresent = -1 ;
3769  if ( lXdialogPresent < 0 )
3770  {
3771  lXdialogPresent = detectPresence("Xdialog") ;
3772  }
3773  return lXdialogPresent && graphicMode( ) ;
3774 }
3775 
3776 
3777 static int gdialogPresent(void)
3778 {
3779  static int lGdialoglPresent = -1 ;
3780  if ( lGdialoglPresent < 0 )
3781  {
3782  lGdialoglPresent = detectPresence( "gdialog" ) ;
3783  }
3784  return lGdialoglPresent && graphicMode( ) ;
3785 }
3786 
3787 
3788 static int osascriptPresent(void)
3789 {
3790  static int lOsascriptPresent = -1 ;
3791  if ( lOsascriptPresent < 0 )
3792  {
3793  gWarningDisplayed |= !!getenv("SSH_TTY");
3794  lOsascriptPresent = detectPresence( "osascript" ) ;
3795  }
3796  return lOsascriptPresent && graphicMode() && !getenv("SSH_TTY") ;
3797 }
3798 
3799 
3800 static int qarmaPresent(void)
3801 {
3802  static int lQarmaPresent = -1 ;
3803  if ( lQarmaPresent < 0 )
3804  {
3805  lQarmaPresent = detectPresence("qarma") ;
3806  }
3807  return lQarmaPresent && graphicMode( ) ;
3808 }
3809 
3810 
3811 static int matedialogPresent(void)
3812 {
3813  static int lMatedialogPresent = -1 ;
3814  if ( lMatedialogPresent < 0 )
3815  {
3816  lMatedialogPresent = detectPresence("matedialog") ;
3817  }
3818  return lMatedialogPresent && graphicMode( ) ;
3819 }
3820 
3821 
3822 static int shellementaryPresent(void)
3823 {
3824  static int lShellementaryPresent = -1 ;
3825  if ( lShellementaryPresent < 0 )
3826  {
3827  lShellementaryPresent = 0 ; /*detectPresence("shellementary"); shellementary is not ready yet */
3828  }
3829  return lShellementaryPresent && graphicMode( ) ;
3830 }
3831 
3832 
3833 static int zenityPresent(void)
3834 {
3835  static int lZenityPresent = -1 ;
3836  if ( lZenityPresent < 0 )
3837  {
3838  lZenityPresent = detectPresence("zenity") ;
3839  }
3840  return lZenityPresent && graphicMode( ) ;
3841 }
3842 
3843 
3844 static int zenity3Present(void)
3845 {
3846  static int lZenity3Present = -1 ;
3847  char lBuff [MAX_PATH_OR_CMD] ;
3848  FILE * lIn ;
3849  int lIntTmp ;
3850 
3851  if ( lZenity3Present < 0 )
3852  {
3853  lZenity3Present = 0 ;
3854  if ( zenityPresent() )
3855  {
3856  lIn = popen( "zenity --version" , "r" ) ;
3857  if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )
3858  {
3859  if ( atoi(lBuff) >= 3 )
3860  {
3861  lZenity3Present = 3 ;
3862  lIntTmp = atoi(strtok(lBuff,".")+2 ) ;
3863  if ( lIntTmp >= 18 )
3864  {
3865  lZenity3Present = 5 ;
3866  }
3867  else if ( lIntTmp >= 10 )
3868  {
3869  lZenity3Present = 4 ;
3870  }
3871  }
3872  else if ( ( atoi(lBuff) == 2 ) && ( atoi(strtok(lBuff,".")+2 ) >= 32 ) )
3873  {
3874  lZenity3Present = 2 ;
3875  }
3876  if (tinyfd_verbose) printf("zenity %d\n", lZenity3Present);
3877  }
3878  pclose( lIn ) ;
3879  }
3880  }
3881  return graphicMode() ? lZenity3Present : 0 ;
3882 }
3883 
3884 
3885 static int kdialogPresent(void)
3886 {
3887  static int lKdialogPresent = -1 ;
3888  char lBuff [MAX_PATH_OR_CMD] ;
3889  FILE * lIn ;
3890  char * lDesktop;
3891 
3892  if ( lKdialogPresent < 0 )
3893  {
3894  if ( zenityPresent() )
3895  {
3896  lDesktop = getenv("XDG_SESSION_DESKTOP");
3897  if ( !lDesktop || ( strcmp(lDesktop, "KDE") && strcmp(lDesktop, "lxqt") ) )
3898  {
3899  lKdialogPresent = 0 ;
3900  return lKdialogPresent ;
3901  }
3902  }
3903 
3904  lKdialogPresent = detectPresence("kdialog") ;
3905  if ( lKdialogPresent && !getenv("SSH_TTY") )
3906  {
3907  lIn = popen( "kdialog --attach 2>&1" , "r" ) ;
3908  if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )
3909  {
3910  if ( ! strstr( "Unknown" , lBuff ) )
3911  {
3912  lKdialogPresent = 2 ;
3913  if (tinyfd_verbose) printf("kdialog-attach %d\n", lKdialogPresent);
3914  }
3915  }
3916  pclose( lIn ) ;
3917 
3918  if (lKdialogPresent == 2)
3919  {
3920  lKdialogPresent = 1 ;
3921  lIn = popen( "kdialog --passivepopup 2>&1" , "r" ) ;
3922  if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )
3923  {
3924  if ( ! strstr( "Unknown" , lBuff ) )
3925  {
3926  lKdialogPresent = 2 ;
3927  if (tinyfd_verbose) printf("kdialog-popup %d\n", lKdialogPresent);
3928  }
3929  }
3930  pclose( lIn ) ;
3931  }
3932  }
3933  }
3934  return graphicMode() ? lKdialogPresent : 0 ;
3935 }
3936 
3937 
3938 static int osx9orBetter(void)
3939 {
3940  static int lOsx9orBetter = -1 ;
3941  char lBuff [MAX_PATH_OR_CMD] ;
3942  FILE * lIn ;
3943  int V,v;
3944 
3945  if ( lOsx9orBetter < 0 )
3946  {
3947  lOsx9orBetter = 0 ;
3948  lIn = popen( "osascript -e 'set osver to system version of (system info)'" , "r" ) ;
3949  if ( ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )
3950  && ( 2 == sscanf(lBuff, "%d.%d", &V, &v) ) )
3951  {
3952  V = V * 100 + v;
3953  if ( V >= 1009 )
3954  {
3955  lOsx9orBetter = 1 ;
3956  }
3957  }
3958  pclose( lIn ) ;
3959  if (tinyfd_verbose) printf("Osx10 = %d, %d = %s\n", lOsx9orBetter, V, lBuff) ;
3960  }
3961  return lOsx9orBetter ;
3962 }
3963 
3964 
3965 static int python2Present(void)
3966 {
3967  static int lPython2Present = -1 ;
3968  int i;
3969 
3970  if ( lPython2Present < 0 )
3971  {
3972  lPython2Present = 0 ;
3973  strcpy(gPython2Name , "python2" ) ;
3974  if ( detectPresence(gPython2Name) ) lPython2Present = 1;
3975  else
3976  {
3977  for ( i = 9 ; i >= 0 ; i -- )
3978  {
3979  sprintf( gPython2Name , "python2.%d" , i ) ;
3981  {
3982  lPython2Present = 1;
3983  break;
3984  }
3985  }
3986  /*if ( ! lPython2Present )
3987  {
3988  strcpy(gPython2Name , "python" ) ;
3989  if ( detectPresence(gPython2Name) ) lPython2Present = 1;
3990  }*/
3991  }
3992  if (tinyfd_verbose) printf("lPython2Present %d\n", lPython2Present) ;
3993  if (tinyfd_verbose) printf("gPython2Name %s\n", gPython2Name) ;
3994  }
3995  return lPython2Present ;
3996 }
3997 
3998 
3999 static int python3Present(void)
4000 {
4001  static int lPython3Present = -1 ;
4002  int i;
4003 
4004  if ( lPython3Present < 0 )
4005  {
4006  lPython3Present = 0 ;
4007  strcpy(gPython3Name , "python3" ) ;
4008  if ( detectPresence(gPython3Name) ) lPython3Present = 1;
4009  else
4010  {
4011  for ( i = 9 ; i >= 0 ; i -- )
4012  {
4013  sprintf( gPython3Name , "python3.%d" , i ) ;
4015  {
4016  lPython3Present = 1;
4017  break;
4018  }
4019  }
4020  /*if ( ! lPython3Present )
4021  {
4022  strcpy(gPython3Name , "python" ) ;
4023  if ( detectPresence(gPython3Name) ) lPython3Present = 1;
4024  }*/
4025  }
4026  if (tinyfd_verbose) printf("lPython3Present %d\n", lPython3Present) ;
4027  if (tinyfd_verbose) printf("gPython3Name %s\n", gPython3Name) ;
4028  }
4029  return lPython3Present ;
4030 }
4031 
4032 
4033 static int tkinter2Present(void)
4034 {
4035  static int lTkinter2Present = -1 ;
4036  char lPythonCommand[256];
4037  char lPythonParams[256] =
4038 "-S -c \"try:\n\timport Tkinter;\nexcept:\n\tprint 0;\"";
4039 
4040 
4041  if ( lTkinter2Present < 0 )
4042  {
4043  lTkinter2Present = 0 ;
4044  if ( python2Present() )
4045  {
4046  sprintf( lPythonCommand , "%s %s" , gPython2Name , lPythonParams ) ;
4047  lTkinter2Present = tryCommand(lPythonCommand) ;
4048  }
4049  if (tinyfd_verbose) printf("lTkinter2Present %d\n", lTkinter2Present) ;
4050  }
4051  return lTkinter2Present && graphicMode() && !(isDarwin() && getenv("SSH_TTY") );
4052 }
4053 
4054 
4055 static int tkinter3Present(void)
4056 {
4057  static int lTkinter3Present = -1 ;
4058  char lPythonCommand[256];
4059  char lPythonParams[256] =
4060  "-S -c \"try:\n\timport tkinter;\nexcept:\n\tprint(0);\"";
4061 
4062  if ( lTkinter3Present < 0 )
4063  {
4064  lTkinter3Present = 0 ;
4065  if ( python3Present() )
4066  {
4067  sprintf( lPythonCommand , "%s %s" , gPython3Name , lPythonParams ) ;
4068  lTkinter3Present = tryCommand(lPythonCommand) ;
4069  }
4070  if (tinyfd_verbose) printf("lTkinter3Present %d\n", lTkinter3Present) ;
4071  }
4072  return lTkinter3Present && graphicMode() && !(isDarwin() && getenv("SSH_TTY") );
4073 }
4074 
4075 
4076 static int pythonDbusPresent(void)
4077 {
4078  static int lDbusPresent = -1 ;
4079  char lPythonCommand[256];
4080  char lPythonParams[256] =
4081 "-c \"try:\n\timport dbus;bus=dbus.SessionBus();\
4082 notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');\
4083 notify=dbus.Interface(notif,'org.freedesktop.Notifications');\nexcept:\n\tprint(0);\"";
4084 
4085  if ( lDbusPresent < 0 )
4086  {
4087  lDbusPresent = 0 ;
4088  if ( python2Present() )
4089  {
4090  strcpy(gPythonName , gPython2Name ) ;
4091  sprintf( lPythonCommand , "%s %s" , gPythonName , lPythonParams ) ;
4092  lDbusPresent = tryCommand(lPythonCommand) ;
4093  }
4094 
4095  if ( ! lDbusPresent && python3Present() )
4096  {
4097  strcpy(gPythonName , gPython3Name ) ;
4098  sprintf( lPythonCommand , "%s %s" , gPythonName , lPythonParams ) ;
4099  lDbusPresent = tryCommand(lPythonCommand) ;
4100  }
4101 
4102  if (tinyfd_verbose) printf("lDbusPresent %d\n", lDbusPresent) ;
4103  if (tinyfd_verbose) printf("gPythonName %s\n", gPythonName) ;
4104  }
4105  return lDbusPresent && graphicMode() && !(isDarwin() && getenv("SSH_TTY") );
4106 }
4107 
4108 
4109 static void sigHandler(int sig)
4110 {
4111  FILE * lIn ;
4112  if ( ( lIn = popen( "pactl unload-module module-sine" , "r" ) ) )
4113  {
4114  pclose( lIn ) ;
4115  }
4116 }
4117 
4118 void tinyfd_beep(void)
4119 {
4120  char lDialogString [256] ;
4121  FILE * lIn ;
4122 
4123  if ( osascriptPresent() )
4124  {
4125  if ( afplayPresent() >= 2 )
4126  {
4127  strcpy( lDialogString , "afplay /System/Library/Sounds/Ping.aiff") ;
4128  }
4129  else
4130  {
4131  strcpy( lDialogString , "osascript -e 'tell application \"System Events\" to beep'") ;
4132  }
4133  }
4134  else if ( pactlPresent() )
4135  {
4136  signal(SIGINT, sigHandler);
4137  /*strcpy( lDialogString , "pactl load-module module-sine frequency=440;sleep .3;pactl unload-module module-sine" ) ;*/
4138  strcpy( lDialogString , "thnum=$(pactl load-module module-sine frequency=440);sleep .3;pactl unload-module $thnum" ) ;
4139  }
4140  else if ( speakertestPresent() )
4141  {
4142  /*strcpy( lDialogString , "timeout -k .3 .3 speaker-test --frequency 440 --test sine > /dev/tty" ) ;*/
4143  strcpy( lDialogString , "( speaker-test -t sine -f 440 > /dev/tty )& pid=$!;sleep .3; kill -9 $pid" ) ;
4144  }
4145  else if ( beepexePresent() )
4146  {
4147  strcpy( lDialogString , "beep.exe 440 300" ) ;
4148  }
4149  else
4150  {
4151  strcpy( lDialogString , "printf '\a' > /dev/tty" ) ;
4152  }
4153 
4154  if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;
4155 
4156  if ( ( lIn = popen( lDialogString , "r" ) ) )
4157  {
4158  pclose( lIn ) ;
4159  }
4160 
4161  if ( pactlPresent() )
4162  {
4163  signal(SIGINT, SIG_DFL);
4164  }
4165 }
4166 
4167 
4169  char const * const aTitle , /* NULL or "" */
4170  char const * const aMessage , /* NULL or "" may contain \n and \t */
4171  char const * const aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */
4172  char const * const aIconType , /* "info" "warning" "error" "question" */
4173  int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */
4174 {
4175  char lBuff [MAX_PATH_OR_CMD] ;
4176  char * lDialogString = NULL ;
4177  char * lpDialogString;
4178  FILE * lIn ;
4179  int lWasGraphicDialog = 0 ;
4180  int lWasXterm = 0 ;
4181  int lResult ;
4182  char lChar ;
4183  struct termios infoOri;
4184  struct termios info;
4185  size_t lTitleLen ;
4186  size_t lMessageLen ;
4187 
4188  lBuff[0]='\0';
4189 
4190  lTitleLen = aTitle ? strlen(aTitle) : 0 ;
4191  lMessageLen = aMessage ? strlen(aMessage) : 0 ;
4192  if ( !aTitle || strcmp(aTitle,"tinyfd_query") )
4193  {
4194  lDialogString = (char *) malloc( MAX_PATH_OR_CMD + lTitleLen + lMessageLen );
4195  }
4196 
4197  if ( osascriptPresent( ) )
4198  {
4199  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return 1;}
4200 
4201  strcpy( lDialogString , "osascript ");
4202  if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'");
4203  strcat( lDialogString , " -e 'try' -e 'set {vButton} to {button returned} of ( display dialog \"") ;
4204  if ( aMessage && strlen(aMessage) )
4205  {
4206  strcat(lDialogString, aMessage) ;
4207  }
4208  strcat(lDialogString, "\" ") ;
4209  if ( aTitle && strlen(aTitle) )
4210  {
4211  strcat(lDialogString, "with title \"") ;
4212  strcat(lDialogString, aTitle) ;
4213  strcat(lDialogString, "\" ") ;
4214  }
4215  strcat(lDialogString, "with icon ") ;
4216  if ( aIconType && ! strcmp( "error" , aIconType ) )
4217  {
4218  strcat(lDialogString, "stop " ) ;
4219  }
4220  else if ( aIconType && ! strcmp( "warning" , aIconType ) )
4221  {
4222  strcat(lDialogString, "caution " ) ;
4223  }
4224  else /* question or info */
4225  {
4226  strcat(lDialogString, "note " ) ;
4227  }
4228  if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) )
4229  {
4230  if ( ! aDefaultButton )
4231  {
4232  strcat( lDialogString ,"default button \"Cancel\" " ) ;
4233  }
4234  }
4235  else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) )
4236  {
4237  strcat( lDialogString ,"buttons {\"No\", \"Yes\"} " ) ;
4238  if (aDefaultButton)
4239  {
4240  strcat( lDialogString ,"default button \"Yes\" " ) ;
4241  }
4242  else
4243  {
4244  strcat( lDialogString ,"default button \"No\" " ) ;
4245  }
4246  strcat( lDialogString ,"cancel button \"No\"" ) ;
4247  }
4248  else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) )
4249  {
4250  strcat( lDialogString ,"buttons {\"No\", \"Yes\", \"Cancel\"} " ) ;
4251  switch (aDefaultButton)
4252  {
4253  case 1: strcat( lDialogString ,"default button \"Yes\" " ) ; break;
4254  case 2: strcat( lDialogString ,"default button \"No\" " ) ; break;
4255  case 0: strcat( lDialogString ,"default button \"Cancel\" " ) ; break;
4256  }
4257  strcat( lDialogString ,"cancel button \"Cancel\"" ) ;
4258  }
4259  else
4260  {
4261  strcat( lDialogString ,"buttons {\"OK\"} " ) ;
4262  strcat( lDialogString ,"default button \"OK\" " ) ;
4263  }
4264  strcat( lDialogString, ")' ") ;
4265 
4266  strcat( lDialogString,
4267 "-e 'if vButton is \"Yes\" then' -e 'return 1'\
4268  -e 'else if vButton is \"OK\" then' -e 'return 1'\
4269  -e 'else if vButton is \"No\" then' -e 'return 2'\
4270  -e 'else' -e 'return 0' -e 'end if' " );
4271 
4272  strcat( lDialogString, "-e 'on error number -128' " ) ;
4273  strcat( lDialogString, "-e '0' " );
4274 
4275  strcat( lDialogString, "-e 'end try'") ;
4276  if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ;
4277  }
4278  else if ( kdialogPresent() )
4279  {
4280  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return 1;}
4281 
4282  strcpy( lDialogString , "kdialog" ) ;
4283  if ( kdialogPresent() == 2 )
4284  {
4285  strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
4286  }
4287 
4288  strcat( lDialogString , " --" ) ;
4289  if ( aDialogType && ( ! strcmp( "okcancel" , aDialogType )
4290  || ! strcmp( "yesno" , aDialogType ) || ! strcmp( "yesnocancel" , aDialogType ) ) )
4291  {
4292  if ( aIconType && ( ! strcmp( "warning" , aIconType )
4293  || ! strcmp( "error" , aIconType ) ) )
4294  {
4295  strcat( lDialogString , "warning" ) ;
4296  }
4297  if ( ! strcmp( "yesnocancel" , aDialogType ) )
4298  {
4299  strcat( lDialogString , "yesnocancel" ) ;
4300  }
4301  else
4302  {
4303  strcat( lDialogString , "yesno" ) ;
4304  }
4305  }
4306  else if ( aIconType && ! strcmp( "error" , aIconType ) )
4307  {
4308  strcat( lDialogString , "error" ) ;
4309  }
4310  else if ( aIconType && ! strcmp( "warning" , aIconType ) )
4311  {
4312  strcat( lDialogString , "sorry" ) ;
4313  }
4314  else
4315  {
4316  strcat( lDialogString , "msgbox" ) ;
4317  }
4318  strcat( lDialogString , " \"" ) ;
4319  if ( aMessage )
4320  {
4321  strcat( lDialogString , aMessage ) ;
4322  }
4323  strcat( lDialogString , "\"" ) ;
4324  if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) )
4325  {
4326  strcat( lDialogString ,
4327  " --yes-label Ok --no-label Cancel" ) ;
4328  }
4329  if ( aTitle && strlen(aTitle) )
4330  {
4331  strcat(lDialogString, " --title \"") ;
4332  strcat(lDialogString, aTitle) ;
4333  strcat(lDialogString, "\"") ;
4334  }
4335 
4336  if ( ! strcmp( "yesnocancel" , aDialogType ) )
4337  {
4338  strcat( lDialogString , "; x=$? ;if [ $x = 0 ] ;then echo 1;elif [ $x = 1 ] ;then echo 2;else echo 0;fi");
4339  }
4340  else
4341  {
4342  strcat( lDialogString , ";if [ $? = 0 ];then echo 1;else echo 0;fi");
4343  }
4344  }
4346  {
4347  if ( zenityPresent() )
4348  {
4349  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return 1;}
4350  strcpy( lDialogString , "szAnswer=$(zenity" ) ;
4351  if ( (zenity3Present() >= 4) && !getenv("SSH_TTY") )
4352  {
4353  strcat(lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
4354  }
4355  }
4356  else if ( matedialogPresent() )
4357  {
4358  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return 1;}
4359  strcpy( lDialogString , "szAnswer=$(matedialog" ) ;
4360  }
4361  else if ( shellementaryPresent() )
4362  {
4363  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return 1;}
4364  strcpy( lDialogString , "szAnswer=$(shellementary" ) ;
4365  }
4366  else
4367  {
4368  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return 1;}
4369  strcpy( lDialogString , "szAnswer=$(qarma" ) ;
4370  if ( !getenv("SSH_TTY") )
4371  {
4372  strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
4373  }
4374  }
4375  strcat(lDialogString, " --");
4376 
4377  if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) )
4378  {
4379  strcat( lDialogString ,
4380  "question --ok-label=Ok --cancel-label=Cancel" ) ;
4381  }
4382  else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) )
4383  {
4384  strcat( lDialogString , "question" ) ;
4385  }
4386  else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) )
4387  {
4388  strcat( lDialogString , "list --column \"\" --hide-header \"Yes\" \"No\"" ) ;
4389  }
4390  else if ( aIconType && ! strcmp( "error" , aIconType ) )
4391  {
4392  strcat( lDialogString , "error" ) ;
4393  }
4394  else if ( aIconType && ! strcmp( "warning" , aIconType ) )
4395  {
4396  strcat( lDialogString , "warning" ) ;
4397  }
4398  else
4399  {
4400  strcat( lDialogString , "info" ) ;
4401  }
4402  if ( aTitle && strlen(aTitle) )
4403  {
4404  strcat(lDialogString, " --title=\"") ;
4405  strcat(lDialogString, aTitle) ;
4406  strcat(lDialogString, "\"") ;
4407  }
4408  if ( aMessage && strlen(aMessage) )
4409  {
4410  strcat(lDialogString, " --no-wrap --text=\"") ;
4411  strcat(lDialogString, aMessage) ;
4412  strcat(lDialogString, "\"") ;
4413  }
4414  if ( (zenity3Present() >= 3) || (!zenityPresent() && (shellementaryPresent() || qarmaPresent()) ) )
4415  {
4416  strcat( lDialogString , " --icon-name=dialog-" ) ;
4417  if ( aIconType && (! strcmp( "question" , aIconType )
4418  || ! strcmp( "error" , aIconType )
4419  || ! strcmp( "warning" , aIconType ) ) )
4420  {
4421  strcat( lDialogString , aIconType ) ;
4422  }
4423  else
4424  {
4425  strcat( lDialogString , "information" ) ;
4426  }
4427  }
4428 
4429  if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null ");
4430 
4431  if ( ! strcmp( "yesnocancel" , aDialogType ) )
4432  {
4433  strcat( lDialogString ,
4434 ");if [ $? = 1 ];then echo 0;elif [ $szAnswer = \"No\" ];then echo 2;else echo 1;fi");
4435  }
4436  else
4437  {
4438  strcat( lDialogString , ");if [ $? = 0 ];then echo 1;else echo 0;fi");
4439  }
4440  }
4441  else if ( !gxmessagePresent() && !gmessagePresent() && !gdialogPresent() && !xdialogPresent() && tkinter2Present() )
4442  {
4443  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return 1;}
4444 
4445  strcpy( lDialogString , gPython2Name ) ;
4446  if ( ! isTerminalRunning( ) && isDarwin( ) )
4447  {
4448  strcat( lDialogString , " -i" ) ; /* for osx without console */
4449  }
4450 
4451  strcat( lDialogString ,
4452 " -S -c \"import Tkinter,tkMessageBox;root=Tkinter.Tk();root.withdraw();");
4453 
4454  if ( isDarwin( ) )
4455  {
4456  strcat( lDialogString ,
4457 "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \
4458 frontmost of process \\\"Python\\\" to true' ''');");
4459  }
4460 
4461  strcat( lDialogString ,"res=tkMessageBox." ) ;
4462  if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) )
4463  {
4464  strcat( lDialogString , "askokcancel(" ) ;
4465  if ( aDefaultButton )
4466  {
4467  strcat( lDialogString , "default=tkMessageBox.OK," ) ;
4468  }
4469  else
4470  {
4471  strcat( lDialogString , "default=tkMessageBox.CANCEL," ) ;
4472  }
4473  }
4474  else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) )
4475  {
4476  strcat( lDialogString , "askyesno(" ) ;
4477  if ( aDefaultButton )
4478  {
4479  strcat( lDialogString , "default=tkMessageBox.YES," ) ;
4480  }
4481  else
4482  {
4483  strcat( lDialogString , "default=tkMessageBox.NO," ) ;
4484  }
4485  }
4486  else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) )
4487  {
4488  strcat( lDialogString , "askyesnocancel(" ) ;
4489  switch ( aDefaultButton )
4490  {
4491  case 1: strcat( lDialogString , "default=tkMessageBox.YES," ); break;
4492  case 2: strcat( lDialogString , "default=tkMessageBox.NO," ); break;
4493  case 0: strcat( lDialogString , "default=tkMessageBox.CANCEL," ); break;
4494  }
4495  }
4496  else
4497  {
4498  strcat( lDialogString , "showinfo(" ) ;
4499  }
4500 
4501  strcat( lDialogString , "icon='" ) ;
4502  if ( aIconType && (! strcmp( "question" , aIconType )
4503  || ! strcmp( "error" , aIconType )
4504  || ! strcmp( "warning" , aIconType ) ) )
4505  {
4506  strcat( lDialogString , aIconType ) ;
4507  }
4508  else
4509  {
4510  strcat( lDialogString , "info" ) ;
4511  }
4512 
4513  strcat(lDialogString, "',") ;
4514  if ( aTitle && strlen(aTitle) )
4515  {
4516  strcat(lDialogString, "title='") ;
4517  strcat(lDialogString, aTitle) ;
4518  strcat(lDialogString, "',") ;
4519  }
4520  if ( aMessage && strlen(aMessage) )
4521  {
4522  strcat(lDialogString, "message='") ;
4523  lpDialogString = lDialogString + strlen(lDialogString);
4524  replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ;
4525  strcat(lDialogString, "'") ;
4526  }
4527 
4528  if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) )
4529  {
4530  strcat(lDialogString, ");\n\
4531 if res is None :\n\tprint 0\n\
4532 elif res is False :\n\tprint 2\n\
4533 else :\n\tprint 1\n\"" ) ;
4534  }
4535  else
4536  {
4537  strcat(lDialogString, ");\n\
4538 if res is False :\n\tprint 0\n\
4539 else :\n\tprint 1\n\"" ) ;
4540  }
4541  }
4542  else if ( !gxmessagePresent() && !gmessagePresent() && !gdialogPresent() && !xdialogPresent() && tkinter3Present() )
4543  {
4544  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return 1;}
4545 
4546  strcpy( lDialogString , gPython3Name ) ;
4547  strcat( lDialogString ,
4548  " -S -c \"import tkinter;from tkinter import messagebox;root=tkinter.Tk();root.withdraw();");
4549 
4550  strcat( lDialogString ,"res=messagebox." ) ;
4551  if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) )
4552  {
4553  strcat( lDialogString , "askokcancel(" ) ;
4554  if ( aDefaultButton )
4555  {
4556  strcat( lDialogString , "default=messagebox.OK," ) ;
4557  }
4558  else
4559  {
4560  strcat( lDialogString , "default=messagebox.CANCEL," ) ;
4561  }
4562  }
4563  else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) )
4564  {
4565  strcat( lDialogString , "askyesno(" ) ;
4566  if ( aDefaultButton )
4567  {
4568  strcat( lDialogString , "default=messagebox.YES," ) ;
4569  }
4570  else
4571  {
4572  strcat( lDialogString , "default=messagebox.NO," ) ;
4573  }
4574  }
4575  else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) )
4576  {
4577  strcat( lDialogString , "askyesnocancel(" ) ;
4578  switch ( aDefaultButton )
4579  {
4580  case 1: strcat( lDialogString , "default=messagebox.YES," ); break;
4581  case 2: strcat( lDialogString , "default=messagebox.NO," ); break;
4582  case 0: strcat( lDialogString , "default=messagebox.CANCEL," ); break;
4583  }
4584  }
4585  else
4586  {
4587  strcat( lDialogString , "showinfo(" ) ;
4588  }
4589 
4590  strcat( lDialogString , "icon='" ) ;
4591  if ( aIconType && (! strcmp( "question" , aIconType )
4592  || ! strcmp( "error" , aIconType )
4593  || ! strcmp( "warning" , aIconType ) ) )
4594  {
4595  strcat( lDialogString , aIconType ) ;
4596  }
4597  else
4598  {
4599  strcat( lDialogString , "info" ) ;
4600  }
4601 
4602  strcat(lDialogString, "',") ;
4603  if ( aTitle && strlen(aTitle) )
4604  {
4605  strcat(lDialogString, "title='") ;
4606  strcat(lDialogString, aTitle) ;
4607  strcat(lDialogString, "',") ;
4608  }
4609  if ( aMessage && strlen(aMessage) )
4610  {
4611  strcat(lDialogString, "message='") ;
4612  lpDialogString = lDialogString + strlen(lDialogString);
4613  replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ;
4614  strcat(lDialogString, "'") ;
4615  }
4616 
4617  if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) )
4618  {
4619  strcat(lDialogString, ");\n\
4620 if res is None :\n\tprint(0)\n\
4621 elif res is False :\n\tprint(2)\n\
4622 else :\n\tprint 1\n\"" ) ;
4623  }
4624  else
4625  {
4626  strcat(lDialogString, ");\n\
4627 if res is False :\n\tprint(0)\n\
4628 else :\n\tprint(1)\n\"" ) ;
4629  }
4630  }
4631  else if ( gxmessagePresent() || gmessagePresent() || (!gdialogPresent() && !xdialogPresent() && xmessagePresent()) )
4632  {
4633  if ( gxmessagePresent() )
4634  {
4635  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gxmessage");return 1;}
4636  strcpy( lDialogString , "gxmessage");
4637  }
4638  else if ( gmessagePresent() )
4639  {
4640  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gmessage");return 1;}
4641  strcpy( lDialogString , "gmessage");
4642  }
4643  else
4644  {
4645  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xmessage");return 1;}
4646  strcpy( lDialogString , "xmessage");
4647  }
4648 
4649  if ( aDialogType && ! strcmp("okcancel" , aDialogType) )
4650  {
4651  strcat( lDialogString , " -buttons Ok:1,Cancel:0");
4652  switch ( aDefaultButton )
4653  {
4654  case 1: strcat( lDialogString , " -default Ok"); break;
4655  case 0: strcat( lDialogString , " -default Cancel"); break;
4656  }
4657  }
4658  else if ( aDialogType && ! strcmp("yesno" , aDialogType) )
4659  {
4660  strcat( lDialogString , " -buttons Yes:1,No:0");
4661  switch ( aDefaultButton )
4662  {
4663  case 1: strcat( lDialogString , " -default Yes"); break;
4664  case 0: strcat( lDialogString , " -default No"); break;
4665  }
4666  }
4667  else if ( aDialogType && ! strcmp("yesnocancel" , aDialogType) )
4668  {
4669  strcat( lDialogString , " -buttons Yes:1,No:2,Cancel:0");
4670  switch ( aDefaultButton )
4671  {
4672  case 1: strcat( lDialogString , " -default Yes"); break;
4673  case 2: strcat( lDialogString , " -default No"); break;
4674  case 0: strcat( lDialogString , " -default Cancel"); break;
4675  }
4676  }
4677  else
4678  {
4679  strcat( lDialogString , " -buttons Ok:1");
4680  strcat( lDialogString , " -default Ok");
4681  }
4682 
4683  strcat( lDialogString , " -center \"");
4684  if ( aMessage && strlen(aMessage) )
4685  {
4686  strcat( lDialogString , aMessage ) ;
4687  }
4688  strcat(lDialogString, "\"" ) ;
4689  if ( aTitle && strlen(aTitle) )
4690  {
4691  strcat( lDialogString , " -title \"");
4692  strcat( lDialogString , aTitle ) ;
4693  strcat( lDialogString, "\"" ) ;
4694  }
4695  strcat( lDialogString , " ; echo $? ");
4696  }
4697  else if ( xdialogPresent() || gdialogPresent() || dialogName() || whiptailPresent() )
4698  {
4699  if ( gdialogPresent( ) )
4700  {
4701  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gdialog");return 1;}
4702  lWasGraphicDialog = 1 ;
4703  strcpy( lDialogString , "(gdialog " ) ;
4704  }
4705  else if ( xdialogPresent( ) )
4706  {
4707  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return 1;}
4708  lWasGraphicDialog = 1 ;
4709  strcpy( lDialogString , "(Xdialog " ) ;
4710  }
4711  else if ( dialogName( ) )
4712  {
4713  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return 0;}
4714  if ( isTerminalRunning( ) )
4715  {
4716  strcpy( lDialogString , "(dialog " ) ;
4717  }
4718  else
4719  {
4720  lWasXterm = 1 ;
4721  strcpy( lDialogString , terminalName() ) ;
4722  strcat( lDialogString , "'(" ) ;
4723  strcat( lDialogString , dialogName() ) ;
4724  strcat( lDialogString , " " ) ;
4725  }
4726  }
4727  else if ( isTerminalRunning( ) )
4728  {
4729  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return 0;}
4730  strcpy( lDialogString , "(whiptail " ) ;
4731  }
4732  else
4733  {
4734  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return 0;}
4735  lWasXterm = 1 ;
4736  strcpy( lDialogString , terminalName() ) ;
4737  strcat( lDialogString , "'(whiptail " ) ;
4738  }
4739 
4740  if ( aTitle && strlen(aTitle) )
4741  {
4742  strcat(lDialogString, "--title \"") ;
4743  strcat(lDialogString, aTitle) ;
4744  strcat(lDialogString, "\" ") ;
4745  }
4746 
4747  if ( !xdialogPresent() && !gdialogPresent() )
4748  {
4749  if ( aDialogType && ( !strcmp( "okcancel" , aDialogType ) || !strcmp( "yesno" , aDialogType )
4750  || !strcmp( "yesnocancel" , aDialogType ) ) )
4751  {
4752  strcat(lDialogString, "--backtitle \"") ;
4753  strcat(lDialogString, "tab: move focus") ;
4754  strcat(lDialogString, "\" ") ;
4755  }
4756  }
4757 
4758  if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) )
4759  {
4760  if ( ! aDefaultButton )
4761  {
4762  strcat( lDialogString , "--defaultno " ) ;
4763  }
4764  strcat( lDialogString ,
4765  "--yes-label \"Ok\" --no-label \"Cancel\" --yesno " ) ;
4766  }
4767  else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) )
4768  {
4769  if ( ! aDefaultButton )
4770  {
4771  strcat( lDialogString , "--defaultno " ) ;
4772  }
4773  strcat( lDialogString , "--yesno " ) ;
4774  }
4775  else if (aDialogType && !strcmp("yesnocancel", aDialogType))
4776  {
4777  if (!aDefaultButton)
4778  {
4779  strcat(lDialogString, "--defaultno ");
4780  }
4781  strcat(lDialogString, "--menu ");
4782  }
4783  else
4784  {
4785  strcat( lDialogString , "--msgbox " ) ;
4786 
4787  }
4788  strcat( lDialogString , "\"" ) ;
4789  if ( aMessage && strlen(aMessage) )
4790  {
4791  strcat(lDialogString, aMessage) ;
4792  }
4793  strcat(lDialogString, "\" ");
4794 
4795  if ( lWasGraphicDialog )
4796  {
4797  if (aDialogType && !strcmp("yesnocancel", aDialogType))
4798  {
4799  strcat(lDialogString,"0 60 0 Yes \"\" No \"\") 2>/tmp/tinyfd.txt;\
4800 if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\
4801 tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ;
4802  }
4803  else
4804  {
4805  strcat(lDialogString,
4806  "10 60 ) 2>&1;if [ $? = 0 ];then echo 1;else echo 0;fi");
4807  }
4808  }
4809  else
4810  {
4811  if (aDialogType && !strcmp("yesnocancel", aDialogType))
4812  {
4813  strcat(lDialogString,"0 60 0 Yes \"\" No \"\" >/dev/tty ) 2>/tmp/tinyfd.txt;\
4814  if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\
4815  tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ;
4816 
4817  if ( lWasXterm )
4818  {
4819  strcat(lDialogString," >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt");
4820  }
4821  else
4822  {
4823  strcat(lDialogString, "; clear >/dev/tty") ;
4824  }
4825  }
4826  else
4827  {
4828  strcat(lDialogString, "10 60 >/dev/tty) 2>&1;if [ $? = 0 ];");
4829  if ( lWasXterm )
4830  {
4831  strcat( lDialogString ,
4832 "then\n\techo 1\nelse\n\techo 0\nfi >/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt");
4833  }
4834  else
4835  {
4836  strcat(lDialogString,
4837  "then echo 1;else echo 0;fi;clear >/dev/tty");
4838  }
4839  }
4840  }
4841  }
4842  else if ( !isTerminalRunning() && terminalName() )
4843  {
4844  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return 0;}
4845  strcpy( lDialogString , terminalName() ) ;
4846  strcat( lDialogString , "'" ) ;
4848  {
4849  gWarningDisplayed = 1 ;
4850  strcat( lDialogString , "echo \"" ) ;
4851  strcat( lDialogString, gTitle) ;
4852  strcat( lDialogString , "\";" ) ;
4853  strcat( lDialogString , "echo \"" ) ;
4854  strcat( lDialogString, tinyfd_needs) ;
4855  strcat( lDialogString , "\";echo;echo;" ) ;
4856  }
4857  if ( aTitle && strlen(aTitle) )
4858  {
4859  strcat( lDialogString , "echo \"" ) ;
4860  strcat( lDialogString, aTitle) ;
4861  strcat( lDialogString , "\";echo;" ) ;
4862  }
4863  if ( aMessage && strlen(aMessage) )
4864  {
4865  strcat( lDialogString , "echo \"" ) ;
4866  strcat( lDialogString, aMessage) ;
4867  strcat( lDialogString , "\"; " ) ;
4868  }
4869  if ( aDialogType && !strcmp("yesno",aDialogType) )
4870  {
4871  strcat( lDialogString , "echo -n \"y/n: \"; " ) ;
4872  strcat( lDialogString , "stty sane -echo;" ) ;
4873  strcat( lDialogString ,
4874  "answer=$( while ! head -c 1 | grep -i [ny];do true ;done);");
4875  strcat( lDialogString ,
4876  "if echo \"$answer\" | grep -iq \"^y\";then\n");
4877  strcat( lDialogString , "\techo 1\nelse\n\techo 0\nfi" ) ;
4878  }
4879  else if ( aDialogType && !strcmp("okcancel",aDialogType) )
4880  {
4881  strcat( lDialogString , "echo -n \"[O]kay/[C]ancel: \"; " ) ;
4882  strcat( lDialogString , "stty sane -echo;" ) ;
4883  strcat( lDialogString ,
4884  "answer=$( while ! head -c 1 | grep -i [oc];do true ;done);");
4885  strcat( lDialogString ,
4886  "if echo \"$answer\" | grep -iq \"^o\";then\n");
4887  strcat( lDialogString , "\techo 1\nelse\n\techo 0\nfi" ) ;
4888  }
4889  else if ( aDialogType && !strcmp("yesnocancel",aDialogType) )
4890  {
4891  strcat( lDialogString , "echo -n \"[Y]es/[N]o/[C]ancel: \"; " ) ;
4892  strcat( lDialogString , "stty sane -echo;" ) ;
4893  strcat( lDialogString ,
4894  "answer=$( while ! head -c 1 | grep -i [nyc];do true ;done);");
4895  strcat( lDialogString ,
4896  "if echo \"$answer\" | grep -iq \"^y\";then\n\techo 1\n");
4897  strcat( lDialogString , "elif echo \"$answer\" | grep -iq \"^n\";then\n\techo 2\n" ) ;
4898  strcat( lDialogString , "else\n\techo 0\nfi" ) ;
4899  }
4900  else
4901  {
4902  strcat(lDialogString , "echo -n \"press enter to continue \"; ");
4903  strcat( lDialogString , "stty sane -echo;" ) ;
4904  strcat( lDialogString ,
4905  "answer=$( while ! head -c 1;do true ;done);echo 1");
4906  }
4907  strcat( lDialogString ,
4908  " >/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt");
4909  }
4910  else if ( !isTerminalRunning() && pythonDbusPresent() && !strcmp("ok" , aDialogType) )
4911  {
4912  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python-dbus");return 1;}
4913  strcpy( lDialogString , gPythonName ) ;
4914  strcat( lDialogString ," -c \"import dbus;bus=dbus.SessionBus();");
4915  strcat( lDialogString ,"notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');" ) ;
4916  strcat( lDialogString ,"notify=dbus.Interface(notif,'org.freedesktop.Notifications');" ) ;
4917  strcat( lDialogString ,"notify.Notify('',0,'" ) ;
4918  if ( aIconType && strlen(aIconType) )
4919  {
4920  strcat( lDialogString , aIconType ) ;
4921  }
4922  strcat(lDialogString, "','") ;
4923  if ( aTitle && strlen(aTitle) )
4924  {
4925  strcat(lDialogString, aTitle) ;
4926  }
4927  strcat(lDialogString, "','") ;
4928  if ( aMessage && strlen(aMessage) )
4929  {
4930  lpDialogString = lDialogString + strlen(lDialogString);
4931  replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ;
4932  }
4933  strcat(lDialogString, "','','',5000)\"") ;
4934  }
4935  else if ( !isTerminalRunning() && (perlPresent() >= 2) && !strcmp("ok" , aDialogType) )
4936  {
4937  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"perl-dbus");return 1;}
4938 
4939  sprintf( lDialogString , "perl -e \"use Net::DBus;\
4940  my \\$sessionBus = Net::DBus->session;\
4941  my \\$notificationsService = \\$sessionBus->get_service('org.freedesktop.Notifications');\
4942  my \\$notificationsObject = \\$notificationsService->get_object('/org/freedesktop/Notifications',\
4943  'org.freedesktop.Notifications');\
4944  my \\$notificationId;\\$notificationId = \\$notificationsObject->Notify(shift, 0, '%s', '%s', '%s', [], {}, -1);\" ",
4945  aIconType?aIconType:"", aTitle?aTitle:"", aMessage?aMessage:"" ) ;
4946  }
4947  else if ( !isTerminalRunning() && notifysendPresent() && !strcmp("ok" , aDialogType) )
4948  {
4949 
4950  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"notifysend");return 1;}
4951  strcpy( lDialogString , "notify-send" ) ;
4952  if ( aIconType && strlen(aIconType) )
4953  {
4954  strcat( lDialogString , " -i '" ) ;
4955  strcat( lDialogString , aIconType ) ;
4956  strcat( lDialogString , "'" ) ;
4957  }
4958  strcat( lDialogString , " \"" ) ;
4959  if ( aTitle && strlen(aTitle) )
4960  {
4961  strcat(lDialogString, aTitle) ;
4962  strcat( lDialogString , " | " ) ;
4963  }
4964  if ( aMessage && strlen(aMessage) )
4965  {
4966  replaceSubStr( aMessage , "\n\t" , " | " , lBuff ) ;
4967  replaceSubStr( aMessage , "\n" , " | " , lBuff ) ;
4968  replaceSubStr( aMessage , "\t" , " " , lBuff ) ;
4969  strcat(lDialogString, lBuff) ;
4970  }
4971  strcat( lDialogString , "\"" ) ;
4972  }
4973  else
4974  {
4975  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return 0;}
4977  {
4978  gWarningDisplayed = 1 ;
4979  printf("\n\n%s\n", gTitle);
4980  printf("%s\n\n", tinyfd_needs);
4981  }
4982  if ( aTitle && strlen(aTitle) )
4983  {
4984  printf("\n%s\n", aTitle);
4985  }
4986 
4987  tcgetattr(0, &infoOri);
4988  tcgetattr(0, &info);
4989  info.c_lflag &= ~ICANON;
4990  info.c_cc[VMIN] = 1;
4991  info.c_cc[VTIME] = 0;
4992  tcsetattr(0, TCSANOW, &info);
4993  if ( aDialogType && !strcmp("yesno",aDialogType) )
4994  {
4995  do
4996  {
4997  if ( aMessage && strlen(aMessage) )
4998  {
4999  printf("\n%s\n",aMessage);
5000  }
5001  printf("y/n: "); fflush(stdout);
5002  lChar = tolower( getchar() ) ;
5003  printf("\n\n");
5004  }
5005  while ( lChar != 'y' && lChar != 'n' );
5006  lResult = lChar == 'y' ? 1 : 0 ;
5007  }
5008  else if ( aDialogType && !strcmp("okcancel",aDialogType) )
5009  {
5010  do
5011  {
5012  if ( aMessage && strlen(aMessage) )
5013  {
5014  printf("\n%s\n",aMessage);
5015  }
5016  printf("[O]kay/[C]ancel: "); fflush(stdout);
5017  lChar = tolower( getchar() ) ;
5018  printf("\n\n");
5019  }
5020  while ( lChar != 'o' && lChar != 'c' );
5021  lResult = lChar == 'o' ? 1 : 0 ;
5022  }
5023  else if ( aDialogType && !strcmp("yesnocancel",aDialogType) )
5024  {
5025  do
5026  {
5027  if ( aMessage && strlen(aMessage) )
5028  {
5029  printf("\n%s\n",aMessage);
5030  }
5031  printf("[Y]es/[N]o/[C]ancel: "); fflush(stdout);
5032  lChar = tolower( getchar() ) ;
5033  printf("\n\n");
5034  }
5035  while ( lChar != 'y' && lChar != 'n' && lChar != 'c' );
5036  lResult = (lChar == 'y') ? 1 : (lChar == 'n') ? 2 : 0 ;
5037  }
5038  else
5039  {
5040  if ( aMessage && strlen(aMessage) )
5041  {
5042  printf("\n%s\n\n",aMessage);
5043  }
5044  printf("press enter to continue "); fflush(stdout);
5045  getchar() ;
5046  printf("\n\n");
5047  lResult = 1 ;
5048  }
5049  tcsetattr(0, TCSANOW, &infoOri);
5050  free(lDialogString);
5051  return lResult ;
5052  }
5053 
5054  if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;
5055 
5056  if ( ! ( lIn = popen( lDialogString , "r" ) ) )
5057  {
5058  free(lDialogString);
5059  return 0 ;
5060  }
5061  while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )
5062  {}
5063 
5064  pclose( lIn ) ;
5065 
5066  /* printf( "lBuff: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */
5067  if ( lBuff[strlen( lBuff ) -1] == '\n' )
5068  {
5069  lBuff[strlen( lBuff ) -1] = '\0' ;
5070  }
5071  /* printf( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */
5072 
5073  if (aDialogType && !strcmp("yesnocancel", aDialogType))
5074  {
5075  if ( lBuff[0]=='1' )
5076  {
5077  if ( !strcmp( lBuff+1 , "Yes" )) strcpy(lBuff,"1");
5078  else if ( !strcmp( lBuff+1 , "No" )) strcpy(lBuff,"2");
5079  }
5080  }
5081  /* printf( "lBuff2: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */
5082 
5083  lResult = !strcmp( lBuff , "2" ) ? 2 : !strcmp( lBuff , "1" ) ? 1 : 0;
5084 
5085  /* printf( "lResult: %d\n" , lResult ) ; */
5086  free(lDialogString);
5087  return lResult ;
5088 }
5089 
5090 
5091 /* return has only meaning for tinyfd_query */
5093  char const * const aTitle , /* NULL or "" */
5094  char const * const aMessage , /* NULL or "" may contain \n and \t */
5095  char const * const aIconType ) /* "info" "warning" "error" */
5096 {
5097  char lBuff[MAX_PATH_OR_CMD];
5098  char * lDialogString = NULL ;
5099  char * lpDialogString ;
5100  FILE * lIn ;
5101  size_t lTitleLen ;
5102  size_t lMessageLen ;
5103 
5104  if ( getenv("SSH_TTY") )
5105  {
5106  return tinyfd_messageBox(aTitle, aMessage, "ok", aIconType, 0);
5107  }
5108 
5109  lTitleLen = aTitle ? strlen(aTitle) : 0 ;
5110  lMessageLen = aMessage ? strlen(aMessage) : 0 ;
5111  if ( !aTitle || strcmp(aTitle,"tinyfd_query") )
5112  {
5113  lDialogString = (char *) malloc( MAX_PATH_OR_CMD + lTitleLen + lMessageLen );
5114  }
5115 
5116  if ( osascriptPresent( ) )
5117  {
5118  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return 1;}
5119 
5120  strcpy( lDialogString , "osascript ");
5121  if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'");
5122  strcat( lDialogString , " -e 'try' -e 'display notification \"") ;
5123  if ( aMessage && strlen(aMessage) )
5124  {
5125  strcat(lDialogString, aMessage) ;
5126  }
5127  strcat(lDialogString, " \" ") ;
5128  if ( aTitle && strlen(aTitle) )
5129  {
5130  strcat(lDialogString, "with title \"") ;
5131  strcat(lDialogString, aTitle) ;
5132  strcat(lDialogString, "\" ") ;
5133  }
5134 
5135  strcat( lDialogString, "' -e 'end try'") ;
5136  if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ;
5137  }
5138  else if ( kdialogPresent() )
5139  {
5140  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return 1;}
5141  strcpy( lDialogString , "kdialog" ) ;
5142 
5143  if ( aIconType && strlen(aIconType) )
5144  {
5145  strcat( lDialogString , " --icon '" ) ;
5146  strcat( lDialogString , aIconType ) ;
5147  strcat( lDialogString , "'" ) ;
5148  }
5149  if ( aTitle && strlen(aTitle) )
5150  {
5151  strcat( lDialogString , " --title \"" ) ;
5152  strcat( lDialogString , aTitle ) ;
5153  strcat( lDialogString , "\"" ) ;
5154  }
5155 
5156  strcat( lDialogString , " --passivepopup" ) ;
5157  strcat( lDialogString , " \"" ) ;
5158  if ( aMessage )
5159  {
5160  strcat( lDialogString , aMessage ) ;
5161  }
5162  strcat( lDialogString , " \" 5" ) ;
5163  }
5164  else if ( (zenity3Present()>=5) || matedialogPresent() || shellementaryPresent() || qarmaPresent() )
5165  {
5166  /* zenity 2.32 & 3.14 has the notification but with a bug: it doesnt return from it */
5167  /* zenity 3.8 show the notification as an alert ok cancel box */
5168  if ( zenity3Present()>=5 )
5169  {
5170  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return 1;}
5171  strcpy( lDialogString , "zenity" ) ;
5172  }
5173  else if ( matedialogPresent() )
5174  {
5175  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return 1;}
5176  strcpy( lDialogString , "matedialog" ) ;
5177  }
5178  else if ( shellementaryPresent() )
5179  {
5180  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return 1;}
5181  strcpy( lDialogString , "shellementary" ) ;
5182  }
5183  else
5184  {
5185  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return 1;}
5186  strcpy( lDialogString , "qarma" ) ;
5187  }
5188 
5189  strcat( lDialogString , " --notification");
5190 
5191  if ( aIconType && strlen( aIconType ) )
5192  {
5193  strcat( lDialogString , " --window-icon '");
5194  strcat( lDialogString , aIconType ) ;
5195  strcat( lDialogString , "'" ) ;
5196  }
5197 
5198  strcat( lDialogString , " --text \"" ) ;
5199  if ( aTitle && strlen(aTitle) )
5200  {
5201  strcat(lDialogString, aTitle) ;
5202  strcat(lDialogString, "\n") ;
5203  }
5204  if ( aMessage && strlen( aMessage ) )
5205  {
5206  strcat( lDialogString , aMessage ) ;
5207  }
5208  strcat( lDialogString , " \"" ) ;
5209  }
5210  else if ( perlPresent() >= 2 )
5211  {
5212  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"perl-dbus");return 1;}
5213  sprintf( lDialogString , "perl -e \"use Net::DBus;\
5214  my \\$sessionBus = Net::DBus->session;\
5215  my \\$notificationsService = \\$sessionBus->get_service('org.freedesktop.Notifications');\
5216  my \\$notificationsObject = \\$notificationsService->get_object('/org/freedesktop/Notifications',\
5217  'org.freedesktop.Notifications');\
5218  my \\$notificationId;\\$notificationId = \\$notificationsObject->Notify(shift, 0, '%s', '%s', '%s', [], {}, -1);\" ",
5219  aIconType?aIconType:"", aTitle?aTitle:"", aMessage?aMessage:"" ) ;
5220  }
5221  else if ( pythonDbusPresent( ) )
5222  {
5223  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python-dbus");return 1;}
5224  strcpy( lDialogString , gPythonName ) ;
5225  strcat( lDialogString ," -c \"import dbus;bus=dbus.SessionBus();");
5226  strcat( lDialogString ,"notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');" ) ;
5227  strcat( lDialogString ,"notify=dbus.Interface(notif,'org.freedesktop.Notifications');" ) ;
5228  strcat( lDialogString ,"notify.Notify('',0,'" ) ;
5229  if ( aIconType && strlen(aIconType) )
5230  {
5231  strcat( lDialogString , aIconType ) ;
5232  }
5233  strcat(lDialogString, "','") ;
5234  if ( aTitle && strlen(aTitle) )
5235  {
5236  strcat(lDialogString, aTitle) ;
5237  }
5238  strcat(lDialogString, "','") ;
5239  if ( aMessage && strlen(aMessage) )
5240  {
5241  lpDialogString = lDialogString + strlen(lDialogString);
5242  replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ;
5243  }
5244  strcat(lDialogString, "','','',5000)\"") ;
5245  }
5246  else if ( notifysendPresent() )
5247  {
5248  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"notifysend");return 1;}
5249  strcpy( lDialogString , "notify-send" ) ;
5250  if ( aIconType && strlen(aIconType) )
5251  {
5252  strcat( lDialogString , " -i '" ) ;
5253  strcat( lDialogString , aIconType ) ;
5254  strcat( lDialogString , "'" ) ;
5255  }
5256  strcat( lDialogString , " \"" ) ;
5257  if ( aTitle && strlen(aTitle) )
5258  {
5259  strcat(lDialogString, aTitle) ;
5260  strcat( lDialogString , " | " ) ;
5261  }
5262  if ( aMessage && strlen(aMessage) )
5263  {
5264  replaceSubStr( aMessage , "\n\t" , " | " , lBuff ) ;
5265  replaceSubStr( aMessage , "\n" , " | " , lBuff ) ;
5266  replaceSubStr( aMessage , "\t" , " " , lBuff ) ;
5267  strcat(lDialogString, lBuff) ;
5268  }
5269  strcat( lDialogString , "\"" ) ;
5270  }
5271  else
5272  {
5273  return tinyfd_messageBox(aTitle, aMessage, "ok", aIconType, 0);
5274  }
5275 
5276  if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;
5277 
5278  if ( ! ( lIn = popen( lDialogString , "r" ) ) )
5279  {
5280  free(lDialogString);
5281  return 0 ;
5282  }
5283 
5284  pclose( lIn ) ;
5285  free(lDialogString);
5286  return 1;
5287 }
5288 
5289 
5290 /* returns NULL on cancel */
5291 char const * tinyfd_inputBox(
5292  char const * const aTitle , /* NULL or "" */
5293  char const * const aMessage , /* NULL or "" may NOT contain \n nor \t */
5294  char const * const aDefaultInput ) /* "" , if NULL it's a passwordBox */
5295 {
5296  static char lBuff[MAX_PATH_OR_CMD];
5297  char * lDialogString = NULL;
5298  char * lpDialogString;
5299  FILE * lIn ;
5300  int lResult ;
5301  int lWasGdialog = 0 ;
5302  int lWasGraphicDialog = 0 ;
5303  int lWasXterm = 0 ;
5304  int lWasBasicXterm = 0 ;
5305  struct termios oldt ;
5306  struct termios newt ;
5307  char * lEOF;
5308  size_t lTitleLen ;
5309  size_t lMessageLen ;
5310 
5311  lBuff[0]='\0';
5312 
5313  lTitleLen = aTitle ? strlen(aTitle) : 0 ;
5314  lMessageLen = aMessage ? strlen(aMessage) : 0 ;
5315  if ( !aTitle || strcmp(aTitle,"tinyfd_query") )
5316  {
5317  lDialogString = (char *) malloc( MAX_PATH_OR_CMD + lTitleLen + lMessageLen );
5318  }
5319 
5320  if ( osascriptPresent( ) )
5321  {
5322  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)1;}
5323  strcpy( lDialogString , "osascript ");
5324  if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'");
5325  strcat( lDialogString , " -e 'try' -e 'display dialog \"") ;
5326  if ( aMessage && strlen(aMessage) )
5327  {
5328  strcat(lDialogString, aMessage) ;
5329  }
5330  strcat(lDialogString, "\" ") ;
5331  strcat(lDialogString, "default answer \"") ;
5332  if ( aDefaultInput && strlen(aDefaultInput) )
5333  {
5334  strcat(lDialogString, aDefaultInput) ;
5335  }
5336  strcat(lDialogString, "\" ") ;
5337  if ( ! aDefaultInput )
5338  {
5339  strcat(lDialogString, "hidden answer true ") ;
5340  }
5341  if ( aTitle && strlen(aTitle) )
5342  {
5343  strcat(lDialogString, "with title \"") ;
5344  strcat(lDialogString, aTitle) ;
5345  strcat(lDialogString, "\" ") ;
5346  }
5347  strcat(lDialogString, "with icon note' ") ;
5348  strcat(lDialogString, "-e '\"1\" & text returned of result' " );
5349  strcat(lDialogString, "-e 'on error number -128' " ) ;
5350  strcat(lDialogString, "-e '0' " );
5351  strcat(lDialogString, "-e 'end try'") ;
5352  if ( ! osx9orBetter() ) strcat(lDialogString, " -e 'end tell'") ;
5353  }
5354  else if ( kdialogPresent() )
5355  {
5356  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;}
5357  strcpy( lDialogString , "szAnswer=$(kdialog" ) ;
5358 
5359  if ( kdialogPresent() == 2 )
5360  {
5361  strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
5362  }
5363 
5364  if ( ! aDefaultInput )
5365  {
5366  strcat(lDialogString, " --password ") ;
5367  }
5368  else
5369  {
5370  strcat(lDialogString, " --inputbox ") ;
5371 
5372  }
5373  strcat(lDialogString, "\"") ;
5374  if ( aMessage && strlen(aMessage) )
5375  {
5376  strcat(lDialogString, aMessage ) ;
5377  }
5378  strcat(lDialogString , "\" \"" ) ;
5379  if ( aDefaultInput && strlen(aDefaultInput) )
5380  {
5381  strcat(lDialogString, aDefaultInput ) ;
5382  }
5383  strcat(lDialogString , "\"" ) ;
5384  if ( aTitle && strlen(aTitle) )
5385  {
5386  strcat(lDialogString, " --title \"") ;
5387  strcat(lDialogString, aTitle) ;
5388  strcat(lDialogString, "\"") ;
5389  }
5390  strcat( lDialogString ,
5391  ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi");
5392  }
5394  {
5395  if ( zenityPresent() )
5396  {
5397  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;}
5398  strcpy( lDialogString , "szAnswer=$(zenity" ) ;
5399  if ( (zenity3Present() >= 4) && !getenv("SSH_TTY") )
5400  {
5401  strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
5402  }
5403  }
5404  else if ( matedialogPresent() )
5405  {
5406  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;}
5407  strcpy( lDialogString , "szAnswer=$(matedialog" ) ;
5408  }
5409  else if ( shellementaryPresent() )
5410  {
5411  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char const *)1;}
5412  strcpy( lDialogString , "szAnswer=$(shellementary" ) ;
5413  }
5414  else
5415  {
5416  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char const *)1;}
5417  strcpy( lDialogString , "szAnswer=$(qarma" ) ;
5418  if ( !getenv("SSH_TTY") )
5419  {
5420  strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
5421  }
5422  }
5423  strcat( lDialogString ," --entry" ) ;
5424 
5425  if ( aTitle && strlen(aTitle) )
5426  {
5427  strcat(lDialogString, " --title=\"") ;
5428  strcat(lDialogString, aTitle) ;
5429  strcat(lDialogString, "\"") ;
5430  }
5431  if ( aMessage && strlen(aMessage) )
5432  {
5433  strcat(lDialogString, " --text=\"") ;
5434  strcat(lDialogString, aMessage) ;
5435  strcat(lDialogString, "\"") ;
5436  }
5437  if ( aDefaultInput && strlen(aDefaultInput) )
5438  {
5439  strcat(lDialogString, " --entry-text=\"") ;
5440  strcat(lDialogString, aDefaultInput) ;
5441  strcat(lDialogString, "\"") ;
5442  }
5443  else
5444  {
5445  strcat(lDialogString, " --hide-text") ;
5446  }
5447  if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null ");
5448  strcat( lDialogString ,
5449  ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi");
5450  }
5451  else if ( gxmessagePresent() || gmessagePresent() )
5452  {
5453  if ( gxmessagePresent() ) {
5454  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gxmessage");return (char const *)1;}
5455  strcpy( lDialogString , "szAnswer=$(gxmessage -buttons Ok:1,Cancel:0 -center \"");
5456  }
5457  else
5458  {
5459  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gmessage");return (char const *)1;}
5460  strcpy( lDialogString , "szAnswer=$(gmessage -buttons Ok:1,Cancel:0 -center \"");
5461  }
5462 
5463  if ( aMessage && strlen(aMessage) )
5464  {
5465  strcat( lDialogString , aMessage ) ;
5466  }
5467  strcat(lDialogString, "\"" ) ;
5468  if ( aTitle && strlen(aTitle) )
5469  {
5470  strcat( lDialogString , " -title \"");
5471  strcat( lDialogString , aTitle ) ;
5472  strcat(lDialogString, "\" " ) ;
5473  }
5474  strcat(lDialogString, " -entrytext \"" ) ;
5475  if ( aDefaultInput && strlen(aDefaultInput) )
5476  {
5477  strcat( lDialogString , aDefaultInput ) ;
5478  }
5479  strcat(lDialogString, "\"" ) ;
5480  strcat( lDialogString , ");echo $?$szAnswer");
5481  }
5482  else if ( !gdialogPresent() && !xdialogPresent() && tkinter2Present( ) )
5483  {
5484  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char const *)1;}
5485  strcpy( lDialogString , gPython2Name ) ;
5486  if ( ! isTerminalRunning( ) && isDarwin( ) )
5487  {
5488  strcat( lDialogString , " -i" ) ; /* for osx without console */
5489  }
5490 
5491  strcat( lDialogString ,
5492 " -S -c \"import Tkinter,tkSimpleDialog;root=Tkinter.Tk();root.withdraw();");
5493 
5494  if ( isDarwin( ) )
5495  {
5496  strcat( lDialogString ,
5497 "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \
5498 frontmost of process \\\"Python\\\" to true' ''');");
5499  }
5500 
5501  strcat( lDialogString ,"res=tkSimpleDialog.askstring(" ) ;
5502  if ( aTitle && strlen(aTitle) )
5503  {
5504  strcat(lDialogString, "title='") ;
5505  strcat(lDialogString, aTitle) ;
5506  strcat(lDialogString, "',") ;
5507  }
5508  if ( aMessage && strlen(aMessage) )
5509  {
5510 
5511  strcat(lDialogString, "prompt='") ;
5512  lpDialogString = lDialogString + strlen(lDialogString);
5513  replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ;
5514  strcat(lDialogString, "',") ;
5515  }
5516  if ( aDefaultInput )
5517  {
5518  if ( strlen(aDefaultInput) )
5519  {
5520  strcat(lDialogString, "initialvalue='") ;
5521  strcat(lDialogString, aDefaultInput) ;
5522  strcat(lDialogString, "',") ;
5523  }
5524  }
5525  else
5526  {
5527  strcat(lDialogString, "show='*'") ;
5528  }
5529  strcat(lDialogString, ");\nif res is None :\n\tprint 0");
5530  strcat(lDialogString, "\nelse :\n\tprint '1'+res\n\"" ) ;
5531  }
5532  else if ( !gdialogPresent() && !xdialogPresent() && tkinter3Present( ) )
5533  {
5534  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char const *)1;}
5535  strcpy( lDialogString , gPython3Name ) ;
5536  strcat( lDialogString ,
5537  " -S -c \"import tkinter; from tkinter import simpledialog;root=tkinter.Tk();root.withdraw();");
5538  strcat( lDialogString ,"res=simpledialog.askstring(" ) ;
5539  if ( aTitle && strlen(aTitle) )
5540  {
5541  strcat(lDialogString, "title='") ;
5542  strcat(lDialogString, aTitle) ;
5543  strcat(lDialogString, "',") ;
5544  }
5545  if ( aMessage && strlen(aMessage) )
5546  {
5547 
5548  strcat(lDialogString, "prompt='") ;
5549  lpDialogString = lDialogString + strlen(lDialogString);
5550  replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ;
5551  strcat(lDialogString, "',") ;
5552  }
5553  if ( aDefaultInput )
5554  {
5555  if ( strlen(aDefaultInput) )
5556  {
5557  strcat(lDialogString, "initialvalue='") ;
5558  strcat(lDialogString, aDefaultInput) ;
5559  strcat(lDialogString, "',") ;
5560  }
5561  }
5562  else
5563  {
5564  strcat(lDialogString, "show='*'") ;
5565  }
5566  strcat(lDialogString, ");\nif res is None :\n\tprint(0)");
5567  strcat(lDialogString, "\nelse :\n\tprint('1'+res)\n\"" ) ;
5568  }
5569  else if ( gdialogPresent() || xdialogPresent() || dialogName() || whiptailPresent() )
5570  {
5571  if ( gdialogPresent( ) )
5572  {
5573  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gdialog");return (char const *)1;}
5574  lWasGraphicDialog = 1 ;
5575  lWasGdialog = 1 ;
5576  strcpy( lDialogString , "(gdialog " ) ;
5577  }
5578  else if ( xdialogPresent( ) )
5579  {
5580  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char const *)1;}
5581  lWasGraphicDialog = 1 ;
5582  strcpy( lDialogString , "(Xdialog " ) ;
5583  }
5584  else if ( dialogName( ) )
5585  {
5586  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;}
5587  if ( isTerminalRunning( ) )
5588  {
5589  strcpy( lDialogString , "(dialog " ) ;
5590  }
5591  else
5592  {
5593  lWasXterm = 1 ;
5594  strcpy( lDialogString , terminalName() ) ;
5595  strcat( lDialogString , "'(" ) ;
5596  strcat( lDialogString , dialogName() ) ;
5597  strcat( lDialogString , " " ) ;
5598  }
5599  }
5600  else if ( isTerminalRunning( ) )
5601  {
5602  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return (char const *)0;}
5603  strcpy( lDialogString , "(whiptail " ) ;
5604  }
5605  else
5606  {
5607  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return (char const *)0;}
5608  lWasXterm = 1 ;
5609  strcpy( lDialogString , terminalName() ) ;
5610  strcat( lDialogString , "'(whiptail " ) ;
5611  }
5612 
5613  if ( aTitle && strlen(aTitle) )
5614  {
5615  strcat(lDialogString, "--title \"") ;
5616  strcat(lDialogString, aTitle) ;
5617  strcat(lDialogString, "\" ") ;
5618  }
5619 
5620  if ( !xdialogPresent() && !gdialogPresent() )
5621  {
5622  strcat(lDialogString, "--backtitle \"") ;
5623  strcat(lDialogString, "tab: move focus") ;
5624  if ( ! aDefaultInput && !lWasGdialog )
5625  {
5626  strcat(lDialogString, " (sometimes nothing, no blink nor star, is shown in text field)") ;
5627  }
5628  strcat(lDialogString, "\" ") ;
5629  }
5630 
5631  if ( aDefaultInput || lWasGdialog )
5632  {
5633  strcat( lDialogString , "--inputbox" ) ;
5634  }
5635  else
5636  {
5637  if ( !lWasGraphicDialog && dialogName() && isDialogVersionBetter09b() )
5638  {
5639  strcat( lDialogString , "--insecure " ) ;
5640  }
5641  strcat( lDialogString , "--passwordbox" ) ;
5642  }
5643  strcat( lDialogString , " \"" ) ;
5644  if ( aMessage && strlen(aMessage) )
5645  {
5646  strcat(lDialogString, aMessage) ;
5647  }
5648  strcat(lDialogString,"\" 10 60 ") ;
5649  if ( aDefaultInput && strlen(aDefaultInput) )
5650  {
5651  strcat(lDialogString, "\"") ;
5652  strcat(lDialogString, aDefaultInput) ;
5653  strcat(lDialogString, "\" ") ;
5654  }
5655  if ( lWasGraphicDialog )
5656  {
5657  strcat(lDialogString,") 2>/tmp/tinyfd.txt;\
5658  if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\
5659  tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ;
5660  }
5661  else
5662  {
5663  strcat(lDialogString,">/dev/tty ) 2>/tmp/tinyfd.txt;\
5664  if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\
5665  tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ;
5666 
5667  if ( lWasXterm )
5668  {
5669  strcat(lDialogString," >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt");
5670  }
5671  else
5672  {
5673  strcat(lDialogString, "; clear >/dev/tty") ;
5674  }
5675  }
5676  }
5677  else if ( ! isTerminalRunning( ) && terminalName() )
5678  {
5679  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;}
5680  lWasBasicXterm = 1 ;
5681  strcpy( lDialogString , terminalName() ) ;
5682  strcat( lDialogString , "'" ) ;
5684  {
5685  gWarningDisplayed = 1 ;
5686  tinyfd_messageBox(gTitle,tinyfd_needs,"ok","warning",0);
5687  }
5688  if ( aTitle && strlen(aTitle) && !tinyfd_forceConsole)
5689  {
5690  strcat( lDialogString , "echo \"" ) ;
5691  strcat( lDialogString, aTitle) ;
5692  strcat( lDialogString , "\";echo;" ) ;
5693  }
5694 
5695  strcat( lDialogString , "echo \"" ) ;
5696  if ( aMessage && strlen(aMessage) )
5697  {
5698  strcat( lDialogString, aMessage) ;
5699  }
5700  strcat( lDialogString , "\";read " ) ;
5701  if ( ! aDefaultInput )
5702  {
5703  strcat( lDialogString , "-s " ) ;
5704  }
5705  strcat( lDialogString , "-p \"" ) ;
5706  strcat( lDialogString , "(esc+enter to cancel): \" ANSWER " ) ;
5707  strcat( lDialogString , ";echo 1$ANSWER >/tmp/tinyfd.txt';" ) ;
5708  strcat( lDialogString , "cat -v /tmp/tinyfd.txt");
5709  }
5710  else if ( !gWarningDisplayed && ! isTerminalRunning( ) && ! terminalName() ) {
5711  gWarningDisplayed = 1 ;
5712  tinyfd_messageBox(gTitle,tinyfd_needs,"ok","warning",0);
5713  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"no_solution");return (char const *)0;}
5714  return NULL;
5715  }
5716  else
5717  {
5718  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;}
5720  {
5721  gWarningDisplayed = 1 ;
5722  tinyfd_messageBox(gTitle,tinyfd_needs,"ok","warning",0);
5723  }
5724  if ( aTitle && strlen(aTitle) )
5725  {
5726  printf("\n%s\n", aTitle);
5727  }
5728  if ( aMessage && strlen(aMessage) )
5729  {
5730  printf("\n%s\n",aMessage);
5731  }
5732  printf("(esc+enter to cancel): "); fflush(stdout);
5733  if ( ! aDefaultInput )
5734  {
5735  tcgetattr(STDIN_FILENO, & oldt) ;
5736  newt = oldt ;
5737  newt.c_lflag &= ~ECHO ;
5738  tcsetattr(STDIN_FILENO, TCSANOW, & newt);
5739  }
5740 
5741  lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin);
5742  /* printf("lbuff<%c><%d>\n",lBuff[0],lBuff[0]); */
5743  if ( ! lEOF || (lBuff[0] == '\0') )
5744  {
5745  free(lDialogString);
5746  return NULL;
5747  }
5748 
5749  if ( lBuff[0] == '\n' )
5750  {
5751  lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin);
5752  /* printf("lbuff<%c><%d>\n",lBuff[0],lBuff[0]); */
5753  if ( ! lEOF || (lBuff[0] == '\0') )
5754  {
5755  free(lDialogString);
5756  return NULL;
5757  }
5758  }
5759 
5760  if ( ! aDefaultInput )
5761  {
5762  tcsetattr(STDIN_FILENO, TCSANOW, & oldt);
5763  printf("\n");
5764  }
5765  printf("\n");
5766  if ( strchr(lBuff,27) )
5767  {
5768  free(lDialogString);
5769  return NULL ;
5770  }
5771  if ( lBuff[strlen( lBuff ) -1] == '\n' )
5772  {
5773  lBuff[strlen( lBuff ) -1] = '\0' ;
5774  }
5775  free(lDialogString);
5776  return lBuff ;
5777  }
5778 
5779  if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;
5780  lIn = popen( lDialogString , "r" );
5781  if ( ! lIn )
5782  {
5783  if ( fileExists("/tmp/tinyfd.txt") )
5784  {
5785  wipefile("/tmp/tinyfd.txt");
5786  remove("/tmp/tinyfd.txt");
5787  }
5788  if ( fileExists("/tmp/tinyfd0.txt") )
5789  {
5790  wipefile("/tmp/tinyfd0.txt");
5791  remove("/tmp/tinyfd0.txt");
5792  }
5793  free(lDialogString);
5794  return NULL ;
5795  }
5796  while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )
5797  {}
5798 
5799  pclose( lIn ) ;
5800 
5801  if ( fileExists("/tmp/tinyfd.txt") )
5802  {
5803  wipefile("/tmp/tinyfd.txt");
5804  remove("/tmp/tinyfd.txt");
5805  }
5806  if ( fileExists("/tmp/tinyfd0.txt") )
5807  {
5808  wipefile("/tmp/tinyfd0.txt");
5809  remove("/tmp/tinyfd0.txt");
5810  }
5811 
5812  /* printf( "len Buff: %lu\n" , strlen(lBuff) ) ; */
5813  /* printf( "lBuff0: %s\n" , lBuff ) ; */
5814  if ( lBuff[strlen( lBuff ) -1] == '\n' )
5815  {
5816  lBuff[strlen( lBuff ) -1] = '\0' ;
5817  }
5818  /* printf( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */
5819  if ( lWasBasicXterm )
5820  {
5821  if ( strstr(lBuff,"^[") ) /* esc was pressed */
5822  {
5823  free(lDialogString);
5824  return NULL ;
5825  }
5826  }
5827 
5828  lResult = strncmp( lBuff , "1" , 1) ? 0 : 1 ;
5829  /* printf( "lResult: %d \n" , lResult ) ; */
5830  if ( ! lResult )
5831  {
5832  free(lDialogString);
5833  return NULL ;
5834  }
5835  /* printf( "lBuff+1: %s\n" , lBuff+1 ) ; */
5836  free(lDialogString);
5837 
5838  return lBuff+1 ;
5839 }
5840 
5841 
5843  char const * const aTitle , /* NULL or "" */
5844  char const * const aDefaultPathAndFile , /* NULL or "" */
5845  int const aNumOfFilterPatterns , /* 0 */
5846  char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */
5847  char const * const aSingleFilterDescription ) /* NULL or "image files" */
5848 {
5849 
5850  static char lBuff [MAX_PATH_OR_CMD] ;
5851  char lDialogString [MAX_PATH_OR_CMD] ;
5852  char lString [MAX_PATH_OR_CMD] ;
5853  int i ;
5854  int lWasGraphicDialog = 0 ;
5855  int lWasXterm = 0 ;
5856  char const * p ;
5857  FILE * lIn ;
5858  lBuff[0]='\0';
5859 
5860  if ( osascriptPresent( ) )
5861  {
5862  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)1;}
5863  strcpy( lDialogString , "osascript ");
5864  if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"Finder\"' -e 'Activate'");
5865  strcat( lDialogString , " -e 'try' -e 'POSIX path of ( choose file name " );
5866  if ( aTitle && strlen(aTitle) )
5867  {
5868  strcat(lDialogString, "with prompt \"") ;
5869  strcat(lDialogString, aTitle) ;
5870  strcat(lDialogString, "\" ") ;
5871  }
5872  getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ;
5873  if ( strlen(lString) )
5874  {
5875  strcat(lDialogString, "default location \"") ;
5876  strcat(lDialogString, lString ) ;
5877  strcat(lDialogString , "\" " ) ;
5878  }
5879  getLastName( lString , aDefaultPathAndFile ) ;
5880  if ( strlen(lString) )
5881  {
5882  strcat(lDialogString, "default name \"") ;
5883  strcat(lDialogString, lString ) ;
5884  strcat(lDialogString , "\" " ) ;
5885  }
5886  strcat( lDialogString , ")' " ) ;
5887  strcat(lDialogString, "-e 'on error number -128' " ) ;
5888  strcat(lDialogString, "-e 'end try'") ;
5889  if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ;
5890  }
5891  else if ( kdialogPresent() )
5892  {
5893  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;}
5894 
5895  strcpy( lDialogString , "kdialog" ) ;
5896  if ( kdialogPresent() == 2 )
5897  {
5898  strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
5899  }
5900  strcat( lDialogString , " --getsavefilename " ) ;
5901 
5902  if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) )
5903  {
5904  if ( aDefaultPathAndFile[0] != '/' )
5905  {
5906  strcat(lDialogString, "$PWD/") ;
5907  }
5908  strcat(lDialogString, "\"") ;
5909  strcat(lDialogString, aDefaultPathAndFile ) ;
5910  strcat(lDialogString , "\"" ) ;
5911  }
5912  else
5913  {
5914  strcat(lDialogString, "$PWD/") ;
5915  }
5916 
5917  if ( aNumOfFilterPatterns > 0 )
5918  {
5919  strcat(lDialogString , " \"" ) ;
5920  for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ )
5921  {
5922  strcat( lDialogString , aFilterPatterns [i] ) ;
5923  strcat( lDialogString , " " ) ;
5924  }
5925  if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )
5926  {
5927  strcat( lDialogString , " | " ) ;
5928  strcat( lDialogString , aSingleFilterDescription ) ;
5929  }
5930  strcat( lDialogString , "\"" ) ;
5931  }
5932  if ( aTitle && strlen(aTitle) )
5933  {
5934  strcat(lDialogString, " --title \"") ;
5935  strcat(lDialogString, aTitle) ;
5936  strcat(lDialogString, "\"") ;
5937  }
5938  }
5940  {
5941  if ( zenityPresent() )
5942  {
5943  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;}
5944  strcpy( lDialogString , "zenity" ) ;
5945  if ( (zenity3Present() >= 4) && !getenv("SSH_TTY") )
5946  {
5947  strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
5948  }
5949  }
5950  else if ( matedialogPresent() )
5951  {
5952  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;}
5953  strcpy( lDialogString , "matedialog" ) ;
5954  }
5955  else if ( shellementaryPresent() )
5956  {
5957  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char const *)1;}
5958  strcpy( lDialogString , "shellementary" ) ;
5959  }
5960  else
5961  {
5962  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char const *)1;}
5963  strcpy( lDialogString , "qarma" ) ;
5964  if ( !getenv("SSH_TTY") )
5965  {
5966  strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
5967  }
5968  }
5969  strcat(lDialogString, " --file-selection --save --confirm-overwrite" ) ;
5970 
5971  if ( aTitle && strlen(aTitle) )
5972  {
5973  strcat(lDialogString, " --title=\"") ;
5974  strcat(lDialogString, aTitle) ;
5975  strcat(lDialogString, "\"") ;
5976  }
5977  if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) )
5978  {
5979  strcat(lDialogString, " --filename=\"") ;
5980  strcat(lDialogString, aDefaultPathAndFile) ;
5981  strcat(lDialogString, "\"") ;
5982  }
5983  if ( aNumOfFilterPatterns > 0 )
5984  {
5985  strcat( lDialogString , " --file-filter='" ) ;
5986  if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )
5987  {
5988  strcat( lDialogString , aSingleFilterDescription ) ;
5989  strcat( lDialogString , " | " ) ;
5990  }
5991  for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ )
5992  {
5993  strcat( lDialogString , aFilterPatterns [i] ) ;
5994  strcat( lDialogString , " " ) ;
5995  }
5996  strcat( lDialogString , "' --file-filter='All files | *'" ) ;
5997  }
5998  if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null ");
5999  }
6000  else if ( !xdialogPresent() && tkinter2Present( ) )
6001  {
6002  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char const *)1;}
6003  strcpy( lDialogString , gPython2Name ) ;
6004  if ( ! isTerminalRunning( ) && isDarwin( ))
6005  {
6006  strcat( lDialogString , " -i" ) ; /* for osx without console */
6007  }
6008  strcat( lDialogString ,
6009 " -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();");
6010 
6011  if ( isDarwin( ) )
6012  {
6013  strcat( lDialogString ,
6014 "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set\
6015  frontmost of process \\\"Python\\\" to true' ''');");
6016  }
6017 
6018  strcat( lDialogString , "print tkFileDialog.asksaveasfilename(");
6019  if ( aTitle && strlen(aTitle) )
6020  {
6021  strcat(lDialogString, "title='") ;
6022  strcat(lDialogString, aTitle) ;
6023  strcat(lDialogString, "',") ;
6024  }
6025  if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) )
6026  {
6027  getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ;
6028  if ( strlen(lString) )
6029  {
6030  strcat(lDialogString, "initialdir='") ;
6031  strcat(lDialogString, lString ) ;
6032  strcat(lDialogString , "'," ) ;
6033  }
6034  getLastName( lString , aDefaultPathAndFile ) ;
6035  if ( strlen(lString) )
6036  {
6037  strcat(lDialogString, "initialfile='") ;
6038  strcat(lDialogString, lString ) ;
6039  strcat(lDialogString , "'," ) ;
6040  }
6041  }
6042  if ( ( aNumOfFilterPatterns > 1 )
6043  || ( (aNumOfFilterPatterns == 1) /* test because poor osx behaviour */
6044  && ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) )
6045  {
6046  strcat(lDialogString , "filetypes=(" ) ;
6047  strcat( lDialogString , "('" ) ;
6048  if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )
6049  {
6050  strcat( lDialogString , aSingleFilterDescription ) ;
6051  }
6052  strcat( lDialogString , "',(" ) ;
6053  for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ )
6054  {
6055  strcat( lDialogString , "'" ) ;
6056  strcat( lDialogString , aFilterPatterns [i] ) ;
6057  strcat( lDialogString , "'," ) ;
6058  }
6059  strcat( lDialogString , "))," ) ;
6060  strcat( lDialogString , "('All files','*'))" ) ;
6061  }
6062  strcat( lDialogString , ")\"" ) ;
6063  }
6064  else if ( !xdialogPresent() && tkinter3Present( ) )
6065  {
6066  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char const *)1;}
6067  strcpy( lDialogString , gPython3Name ) ;
6068  strcat( lDialogString ,
6069  " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();");
6070  strcat( lDialogString , "print( filedialog.asksaveasfilename(");
6071  if ( aTitle && strlen(aTitle) )
6072  {
6073  strcat(lDialogString, "title='") ;
6074  strcat(lDialogString, aTitle) ;
6075  strcat(lDialogString, "',") ;
6076  }
6077  if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) )
6078  {
6079  getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ;
6080  if ( strlen(lString) )
6081  {
6082  strcat(lDialogString, "initialdir='") ;
6083  strcat(lDialogString, lString ) ;
6084  strcat(lDialogString , "'," ) ;
6085  }
6086  getLastName( lString , aDefaultPathAndFile ) ;
6087  if ( strlen(lString) )
6088  {
6089  strcat(lDialogString, "initialfile='") ;
6090  strcat(lDialogString, lString ) ;
6091  strcat(lDialogString , "'," ) ;
6092  }
6093  }
6094  if ( ( aNumOfFilterPatterns > 1 )
6095  || ( (aNumOfFilterPatterns == 1) /* test because poor osx behaviour */
6096  && ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) )
6097  {
6098  strcat(lDialogString , "filetypes=(" ) ;
6099  strcat( lDialogString , "('" ) ;
6100  if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )
6101  {
6102  strcat( lDialogString , aSingleFilterDescription ) ;
6103  }
6104  strcat( lDialogString , "',(" ) ;
6105  for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ )
6106  {
6107  strcat( lDialogString , "'" ) ;
6108  strcat( lDialogString , aFilterPatterns [i] ) ;
6109  strcat( lDialogString , "'," ) ;
6110  }
6111  strcat( lDialogString , "))," ) ;
6112  strcat( lDialogString , "('All files','*'))" ) ;
6113  }
6114  strcat( lDialogString , "))\"" ) ;
6115  }
6116  else if ( xdialogPresent() || dialogName() )
6117  {
6118  if ( xdialogPresent( ) )
6119  {
6120  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char const *)1;}
6121  lWasGraphicDialog = 1 ;
6122  strcpy( lDialogString , "(Xdialog " ) ;
6123  }
6124  else if ( isTerminalRunning( ) )
6125  {
6126  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;}
6127  strcpy( lDialogString , "(dialog " ) ;
6128  }
6129  else
6130  {
6131  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;}
6132  lWasXterm = 1 ;
6133  strcpy( lDialogString , terminalName() ) ;
6134  strcat( lDialogString , "'(" ) ;
6135  strcat( lDialogString , dialogName() ) ;
6136  strcat( lDialogString , " " ) ;
6137  }
6138 
6139  if ( aTitle && strlen(aTitle) )
6140  {
6141  strcat(lDialogString, "--title \"") ;
6142  strcat(lDialogString, aTitle) ;
6143  strcat(lDialogString, "\" ") ;
6144  }
6145 
6146  if ( !xdialogPresent() && !gdialogPresent() )
6147  {
6148  strcat(lDialogString, "--backtitle \"") ;
6149  strcat(lDialogString,
6150  "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ;
6151  strcat(lDialogString, "\" ") ;
6152  }
6153 
6154  strcat( lDialogString , "--fselect \"" ) ;
6155  if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) )
6156  {
6157  if ( ! strchr(aDefaultPathAndFile, '/') )
6158  {
6159  strcat(lDialogString, "./") ;
6160  }
6161  strcat(lDialogString, aDefaultPathAndFile) ;
6162  }
6163  else if ( ! isTerminalRunning( ) && !lWasGraphicDialog )
6164  {
6165  strcat(lDialogString, getenv("HOME")) ;
6166  strcat(lDialogString, "/") ;
6167  }
6168  else
6169  {
6170  strcat(lDialogString, "./") ;
6171  }
6172 
6173  if ( lWasGraphicDialog )
6174  {
6175  strcat(lDialogString, "\" 0 60 ) 2>&1 ") ;
6176  }
6177  else
6178  {
6179  strcat(lDialogString, "\" 0 60 >/dev/tty) ") ;
6180  if ( lWasXterm )
6181  {
6182  strcat( lDialogString ,
6183  "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt");
6184  }
6185  else
6186  {
6187  strcat(lDialogString, "2>&1 ; clear >/dev/tty") ;
6188  }
6189  }
6190  }
6191  else
6192  {
6193  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox(aTitle,NULL,NULL);}
6194  p = tinyfd_inputBox( aTitle , "Save file" , "" ) ;
6195  getPathWithoutFinalSlash( lString , p ) ;
6196  if ( strlen( lString ) && ! dirExists( lString ) )
6197  {
6198  return NULL ;
6199  }
6200  getLastName(lString,p);
6201  if ( ! strlen(lString) )
6202  {
6203  return NULL;
6204  }
6205  return p ;
6206  }
6207 
6208  if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;
6209  if ( ! ( lIn = popen( lDialogString , "r" ) ) )
6210  {
6211  return NULL ;
6212  }
6213  while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )
6214  {}
6215  pclose( lIn ) ;
6216  if ( lBuff[strlen( lBuff ) -1] == '\n' )
6217  {
6218  lBuff[strlen( lBuff ) -1] = '\0' ;
6219  }
6220  /* printf( "lBuff: %s\n" , lBuff ) ; */
6221  if ( ! strlen(lBuff) )
6222  {
6223  return NULL;
6224  }
6225  getPathWithoutFinalSlash( lString , lBuff ) ;
6226  if ( strlen( lString ) && ! dirExists( lString ) )
6227  {
6228  return NULL ;
6229  }
6230  getLastName(lString,lBuff);
6231  if ( ! filenameValid(lString) )
6232  {
6233  return NULL;
6234  }
6235  return lBuff ;
6236 }
6237 
6238 
6239 /* in case of multiple files, the separator is | */
6241  char const * const aTitle , /* NULL or "" */
6242  char const * const aDefaultPathAndFile , /* NULL or "" */
6243  int const aNumOfFilterPatterns , /* 0 */
6244  char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */
6245  char const * const aSingleFilterDescription , /* NULL or "image files" */
6246  int const aAllowMultipleSelects ) /* 0 or 1 */
6247 {
6248  static char lBuff [MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD] ;
6249  char lDialogString [MAX_PATH_OR_CMD] ;
6250  char lString [MAX_PATH_OR_CMD] ;
6251  int i ;
6252  FILE * lIn ;
6253  char * p ;
6254  char const * p2 ;
6255  int lWasKdialog = 0 ;
6256  int lWasGraphicDialog = 0 ;
6257  int lWasXterm = 0 ;
6258  lBuff[0]='\0';
6259 
6260  if ( osascriptPresent( ) )
6261  {
6262  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)1;}
6263  strcpy( lDialogString , "osascript ");
6264  if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'");
6265  strcat( lDialogString , " -e 'try' -e '" );
6266  if ( ! aAllowMultipleSelects )
6267  {
6268 
6269 
6270  strcat( lDialogString , "POSIX path of ( " );
6271  }
6272  else
6273  {
6274  strcat( lDialogString , "set mylist to " );
6275  }
6276  strcat( lDialogString , "choose file " );
6277  if ( aTitle && strlen(aTitle) )
6278  {
6279  strcat(lDialogString, "with prompt \"") ;
6280  strcat(lDialogString, aTitle) ;
6281  strcat(lDialogString, "\" ") ;
6282  }
6283  getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ;
6284  if ( strlen(lString) )
6285  {
6286  strcat(lDialogString, "default location \"") ;
6287  strcat(lDialogString, lString ) ;
6288  strcat(lDialogString , "\" " ) ;
6289  }
6290  if ( aNumOfFilterPatterns > 0 )
6291  {
6292  strcat(lDialogString , "of type {\"" );
6293  strcat( lDialogString , aFilterPatterns [0] + 2 ) ;
6294  strcat( lDialogString , "\"" ) ;
6295  for ( i = 1 ; i < aNumOfFilterPatterns ; i ++ )
6296  {
6297  strcat( lDialogString , ",\"" ) ;
6298  strcat( lDialogString , aFilterPatterns [i] + 2) ;
6299  strcat( lDialogString , "\"" ) ;
6300  }
6301  strcat( lDialogString , "} " ) ;
6302  }
6303  if ( aAllowMultipleSelects )
6304  {
6305  strcat( lDialogString , "multiple selections allowed true ' " ) ;
6306  strcat( lDialogString ,
6307  "-e 'set mystring to POSIX path of item 1 of mylist' " );
6308  strcat( lDialogString ,
6309  "-e 'repeat with i from 2 to the count of mylist' " );
6310  strcat( lDialogString , "-e 'set mystring to mystring & \"|\"' " );
6311  strcat( lDialogString ,
6312  "-e 'set mystring to mystring & POSIX path of item i of mylist' " );
6313  strcat( lDialogString , "-e 'end repeat' " );
6314  strcat( lDialogString , "-e 'mystring' " );
6315  }
6316  else
6317  {
6318  strcat( lDialogString , ")' " ) ;
6319  }
6320  strcat(lDialogString, "-e 'on error number -128' " ) ;
6321  strcat(lDialogString, "-e 'end try'") ;
6322  if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ;
6323  }
6324  else if ( kdialogPresent() )
6325  {
6326  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;}
6327  lWasKdialog = 1 ;
6328 
6329  strcpy( lDialogString , "kdialog" ) ;
6330  if ( kdialogPresent() == 2 )
6331  {
6332  strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
6333  }
6334  strcat( lDialogString , " --getopenfilename " ) ;
6335 
6336  if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) )
6337  {
6338  if ( aDefaultPathAndFile[0] != '/' )
6339  {
6340  strcat(lDialogString, "$PWD/") ;
6341  }
6342  strcat(lDialogString, "\"") ;
6343  strcat(lDialogString, aDefaultPathAndFile ) ;
6344  strcat(lDialogString , "\"" ) ;
6345  }
6346  else
6347  {
6348  strcat(lDialogString, "$PWD/") ;
6349  }
6350 
6351  if ( aNumOfFilterPatterns > 0 )
6352  {
6353  strcat(lDialogString , " \"" ) ;
6354  for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ )
6355  {
6356  strcat( lDialogString , aFilterPatterns [i] ) ;
6357  strcat( lDialogString , " " ) ;
6358  }
6359  if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )
6360  {
6361  strcat( lDialogString , " | " ) ;
6362  strcat( lDialogString , aSingleFilterDescription ) ;
6363  }
6364  strcat( lDialogString , "\"" ) ;
6365  }
6366  if ( aAllowMultipleSelects )
6367  {
6368  strcat( lDialogString , " --multiple --separate-output" ) ;
6369  }
6370  if ( aTitle && strlen(aTitle) )
6371  {
6372  strcat(lDialogString, " --title \"") ;
6373  strcat(lDialogString, aTitle) ;
6374  strcat(lDialogString, "\"") ;
6375  }
6376  }
6378  {
6379  if ( zenityPresent() )
6380  {
6381  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;}
6382  strcpy( lDialogString , "zenity" ) ;
6383  if ( (zenity3Present() >= 4) && !getenv("SSH_TTY") )
6384  {
6385  strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
6386  }
6387  }
6388  else if ( matedialogPresent() )
6389  {
6390  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;}
6391  strcpy( lDialogString , "matedialog" ) ;
6392  }
6393  else if ( shellementaryPresent() )
6394  {
6395  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char const *)1;}
6396  strcpy( lDialogString , "shellementary" ) ;
6397  }
6398  else
6399  {
6400  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char const *)1;}
6401  strcpy( lDialogString , "qarma" ) ;
6402  if ( !getenv("SSH_TTY") )
6403  {
6404  strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
6405  }
6406  }
6407  strcat( lDialogString , " --file-selection" ) ;
6408 
6409  if ( aAllowMultipleSelects )
6410  {
6411  strcat( lDialogString , " --multiple" ) ;
6412  }
6413  if ( aTitle && strlen(aTitle) )
6414  {
6415  strcat(lDialogString, " --title=\"") ;
6416  strcat(lDialogString, aTitle) ;
6417  strcat(lDialogString, "\"") ;
6418  }
6419  if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) )
6420  {
6421  strcat(lDialogString, " --filename=\"") ;
6422  strcat(lDialogString, aDefaultPathAndFile) ;
6423  strcat(lDialogString, "\"") ;
6424  }
6425  if ( aNumOfFilterPatterns > 0 )
6426  {
6427  strcat( lDialogString , " --file-filter='" ) ;
6428  if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )
6429  {
6430  strcat( lDialogString , aSingleFilterDescription ) ;
6431  strcat( lDialogString , " | " ) ;
6432  }
6433  for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ )
6434  {
6435  strcat( lDialogString , aFilterPatterns [i] ) ;
6436  strcat( lDialogString , " " ) ;
6437  }
6438  strcat( lDialogString , "' --file-filter='All files | *'" ) ;
6439  }
6440  if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null ");
6441  }
6442  else if ( tkinter2Present( ) )
6443  {
6444  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char const *)1;}
6445  strcpy( lDialogString , gPython2Name ) ;
6446  if ( ! isTerminalRunning( ) && isDarwin( ) )
6447  {
6448  strcat( lDialogString , " -i" ) ; /* for osx without console */
6449  }
6450  strcat( lDialogString ,
6451 " -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();");
6452 
6453  if ( isDarwin( ) )
6454  {
6455  strcat( lDialogString ,
6456 "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \
6457 frontmost of process \\\"Python\\\" to true' ''');");
6458  }
6459  strcat( lDialogString , "lFiles=tkFileDialog.askopenfilename(");
6460  if ( aAllowMultipleSelects )
6461  {
6462  strcat( lDialogString , "multiple=1," ) ;
6463  }
6464  if ( aTitle && strlen(aTitle) )
6465  {
6466  strcat(lDialogString, "title='") ;
6467  strcat(lDialogString, aTitle) ;
6468  strcat(lDialogString, "',") ;
6469  }
6470  if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) )
6471  {
6472  getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ;
6473  if ( strlen(lString) )
6474  {
6475  strcat(lDialogString, "initialdir='") ;
6476  strcat(lDialogString, lString ) ;
6477  strcat(lDialogString , "'," ) ;
6478  }
6479  getLastName( lString , aDefaultPathAndFile ) ;
6480  if ( strlen(lString) )
6481  {
6482  strcat(lDialogString, "initialfile='") ;
6483  strcat(lDialogString, lString ) ;
6484  strcat(lDialogString , "'," ) ;
6485  }
6486  }
6487  if ( ( aNumOfFilterPatterns > 1 )
6488  || ( ( aNumOfFilterPatterns == 1 ) /*test because poor osx behaviour*/
6489  && ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) )
6490  {
6491  strcat(lDialogString , "filetypes=(" ) ;
6492  strcat( lDialogString , "('" ) ;
6493  if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )
6494  {
6495  strcat( lDialogString , aSingleFilterDescription ) ;
6496  }
6497  strcat( lDialogString , "',(" ) ;
6498  for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ )
6499  {