_________ __                 __
        /   _____//  |_____________ _/  |______     ____  __ __  ______
        \_____  \\   __\_  __ \__  \\   __\__  \   / ___\|  |  \/  ___/
        /        \|  |  |  | \// __ \|  |  / __ \_/ /_/  >  |  /\___ \
       /_______  /|__|  |__|  (____  /__| (____  /\___  /|____//____  >
               \/                  \/          \//_____/            \/
    ______________________                           ______________________
                          T H E   W A R   B E G I N S
                   Stratagus - A free fantasy real time strategy game engine

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