themes: Workaround for bug where a background color of RGB 0,0,0 in Black color schem...
[ntk.git] / src / fl_open_uri.cxx
blobadeb4cb917ae2d9fa52851c2cfe0f9a62684def4
1 //
2 // "$Id: fl_open_uri.cxx 8063 2010-12-19 21:20:10Z matt $"
3 //
4 // fl_open_uri() code for FLTK.
5 //
6 // Test with:
7 //
8 // gcc -I/fltk/dir -I/fltk/dir/src -DTEST -o fl_open_uri fl_open_uri.cxx -lfltk
9 //
10 // Copyright 2003-2010 by Michael R Sweet
12 // This library is free software; you can redistribute it and/or
13 // modify it under the terms of the GNU Library General Public
14 // License as published by the Free Software Foundation; either
15 // version 2 of the License, or (at your option) any later version.
17 // This library is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 // Library General Public License for more details.
22 // You should have received a copy of the GNU Library General Public
23 // License along with this library; if not, write to the Free Software
24 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
25 // USA.
29 // Include necessary headers...
32 #include <FL/filename.H>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <errno.h>
36 #include <sys/types.h>
37 #include "flstring.h"
38 #ifdef WIN32
39 # include <windows.h>
40 # include <shellapi.h>
41 #else
42 # include <sys/wait.h>
43 # include <signal.h>
44 # include <fcntl.h>
45 # include <unistd.h>
46 #endif // WIN32
50 // Local functions...
53 #if !defined(WIN32) && !defined(__APPLE__)
54 static char *path_find(const char *program, char *filename, int filesize);
55 #endif // !WIN32 && !__APPLE__
56 #ifndef WIN32
57 static int run_program(const char *program, char **argv, char *msg, int msglen);
58 #endif // !WIN32
60 /** \addtogroup filenames
61 @{ */
63 /**
64 * Opens the specified Uniform Resource Identifier (URI).
65 * Uses an operating-system dependent program or interface. For URIs
66 * using the "ftp", "http", or "https" schemes, the system default web
67 * browser is used to open the URI, while "mailto" and "news" URIs are
68 * typically opened using the system default mail reader and "file" URIs
69 * are opened using the file system navigator.
71 * On success, the (optional) msg buffer is filled with the command that
72 * was run to open the URI; on Windows, this will always be "open uri".
74 * On failure, the msg buffer is filled with an English error message.
76 * \b Example
77 * \code
78 * #include <FL/filename.H>
79 * [..]
80 * char errmsg[512];
81 * if ( !fl_open_uri("http://google.com/", errmsg, sizeof(errmsg)) ) {
82 * char warnmsg[768];
83 * sprintf(warnmsg, "Error: %s", errmsg);
84 * fl_alert(warnmsg);
85 * }
86 * \endcode
88 * @param uri The URI to open
89 * @param msg Optional buffer which contains the command or error message
90 * @param msglen Length of optional buffer
91 * @return 1 on success, 0 on failure
94 int
95 fl_open_uri(const char *uri, char *msg, int msglen) {
96 // Supported URI schemes...
97 static const char * const schemes[] = {
98 "file://",
99 "ftp://",
100 "http://",
101 "https://",
102 "mailto:",
103 "news://",
104 NULL
107 // Validate the URI scheme...
108 int i;
109 for (i = 0; schemes[i]; i ++)
110 if (!strncmp(uri, schemes[i], strlen(schemes[i])))
111 break;
113 if (!schemes[i]) {
114 if (msg) {
115 char scheme[255];
116 if (sscanf(uri, "%254[^:]", scheme) == 1) {
117 snprintf(msg, msglen, "URI scheme \"%s\" not supported.", scheme);
118 } else {
119 snprintf(msg, msglen, "Bad URI \"%s\"", uri);
123 return 0;
126 #ifdef WIN32
127 if (msg) snprintf(msg, msglen, "open %s", uri);
129 return (int)(ShellExecute(HWND_DESKTOP, "open", uri, NULL, NULL, SW_SHOW) > (void *)32);
131 #elif defined(__APPLE__)
132 char *argv[3]; // Command-line arguments
134 argv[0] = (char*)"open";
135 argv[1] = (char*)uri;
136 argv[2] = (char*)0;
138 if (msg) snprintf(msg, msglen, "open %s", uri);
140 return run_program("/usr/bin/open", argv, msg, msglen) != 0;
142 #else // !WIN32 && !__APPLE__
143 // Run any of several well-known commands to open the URI.
145 // We give preference to the Portland group's xdg-utils
146 // programs which run the user's preferred web browser, etc.
147 // based on the current desktop environment in use. We fall
148 // back on older standards and then finally test popular programs
149 // until we find one we can use.
151 // Note that we specifically do not support the MAILER and
152 // BROWSER environment variables because we have no idea whether
153 // we need to run the listed commands in a terminal program.
155 char command[FL_PATH_MAX], // Command to run...
156 *argv[4], // Command-line arguments
157 remote[1024]; // Remote-mode command...
158 const char * const *commands; // Array of commands to check...
159 static const char * const browsers[] = {
160 "xdg-open", // Portland
161 "htmlview", // Freedesktop.org
162 "firefox",
163 "mozilla",
164 "netscape",
165 "konqueror", // KDE
166 "opera",
167 "hotjava", // Solaris
168 "mosaic",
169 NULL
171 static const char * const readers[] = {
172 "xdg-email", // Portland
173 "thunderbird",
174 "mozilla",
175 "netscape",
176 "evolution", // GNOME
177 "kmailservice", // KDE
178 NULL
180 static const char * const managers[] = {
181 "xdg-open", // Portland
182 "fm", // IRIX
183 "dtaction", // CDE
184 "nautilus", // GNOME
185 "konqueror", // KDE
186 NULL
189 // Figure out which commands to check for...
190 if (!strncmp(uri, "file://", 7)) commands = managers;
191 else if (!strncmp(uri, "mailto:", 7) ||
192 !strncmp(uri, "news:", 5)) commands = readers;
193 else commands = browsers;
195 // Find the command to run...
196 for (i = 0; commands[i]; i ++)
197 if (path_find(commands[i], command, sizeof(command))) break;
199 if (!commands[i]) {
200 if (msg) {
201 snprintf(msg, msglen, "No helper application found for \"%s\"", uri);
204 return 0;
207 // Handle command-specific arguments...
208 argv[0] = (char *)commands[i];
210 if (!strcmp(commands[i], "firefox") ||
211 !strcmp(commands[i], "mozilla") ||
212 !strcmp(commands[i], "netscape") ||
213 !strcmp(commands[i], "thunderbird")) {
214 // program -remote openURL(uri)
215 snprintf(remote, sizeof(remote), "openURL(%s)", uri);
217 argv[1] = (char *)"-remote";
218 argv[2] = remote;
219 argv[3] = 0;
220 } else if (!strcmp(commands[i], "dtaction")) {
221 // dtaction open uri
222 argv[1] = (char *)"open";
223 argv[2] = (char *)uri;
224 argv[3] = 0;
225 } else {
226 // program uri
227 argv[1] = (char *)uri;
228 argv[2] = 0;
231 if (msg) {
232 strlcpy(msg, argv[0], msglen);
234 for (i = 1; argv[i]; i ++) {
235 strlcat(msg, " ", msglen);
236 strlcat(msg, argv[i], msglen);
240 return run_program(command, argv, msg, msglen) != 0;
241 #endif // WIN32
244 /** @} */
246 #if !defined(WIN32) && !defined(__APPLE__)
247 // Find a program in the path...
248 static char *path_find(const char *program, char *filename, int filesize) {
249 const char *path; // Search path
250 char *ptr, // Pointer into filename
251 *end; // End of filename buffer
254 if ((path = getenv("PATH")) == NULL) path = "/bin:/usr/bin";
256 for (ptr = filename, end = filename + filesize - 1; *path; path ++) {
257 if (*path == ':') {
258 if (ptr > filename && ptr[-1] != '/' && ptr < end) *ptr++ = '/';
260 strlcpy(ptr, program, end - ptr + 1);
262 if (!access(filename, X_OK)) return filename;
264 ptr = filename;
265 } else if (ptr < end) *ptr++ = *path;
268 if (ptr > filename) {
269 if (ptr[-1] != '/' && ptr < end) *ptr++ = '/';
271 strlcpy(ptr, program, end - ptr + 1);
273 if (!access(filename, X_OK)) return filename;
276 return 0;
278 #endif // !WIN32 && !__APPLE__
281 #ifndef WIN32
282 // Run the specified program, returning 1 on success and 0 on failure
283 static int
284 run_program(const char *program, char **argv, char *msg, int msglen) {
285 pid_t pid; // Process ID of first child
286 int status; // Exit status from first child
287 sigset_t set, oldset; // Signal masks
290 // Block SIGCHLD while we run the program...
292 // Note that I only use the POSIX signal APIs, however older operating
293 // systems may either not support POSIX signals or have side effects.
294 // IRIX, for example, provides three separate and incompatible signal
295 // APIs, so it is possible that an application setting a signal handler
296 // via signal() or sigset() will not have its SIGCHLD signals blocked...
298 sigemptyset(&set);
299 sigaddset(&set, SIGCHLD);
300 sigprocmask(SIG_BLOCK, &set, &oldset);
302 // Create child processes that actually run the program for us...
303 if ((pid = fork()) == 0) {
304 // First child comes here, fork a second child and exit...
305 if (!fork()) {
306 // Second child comes here, redirect stdin/out/err to /dev/null...
307 close(0);
308 open("/dev/null", O_RDONLY);
310 close(1);
311 open("/dev/null", O_WRONLY);
313 close(2);
314 open("/dev/null", O_WRONLY);
316 // Detach from the current process group...
317 setsid();
319 // Run the program...
320 execv(program, argv);
321 _exit(0);
322 } else {
323 // First child gets here, exit immediately...
324 _exit(0);
326 } else if (pid < 0) {
327 // Restore signal handling...
328 sigprocmask(SIG_SETMASK, &oldset, NULL);
330 // Return indicating failure...
331 return 0;
334 // Wait for the first child to exit...
335 while (waitpid(pid, &status, 0) < 0) {
336 if (errno != EINTR) {
337 // Someone else grabbed the child status...
338 if (msg) snprintf(msg, msglen, "waitpid(%ld) failed: %s", (long)pid,
339 strerror(errno));
341 // Restore signal handling...
342 sigprocmask(SIG_SETMASK, &oldset, NULL);
344 // Return indicating failure...
345 return 0;
349 // Restore signal handling...
350 sigprocmask(SIG_SETMASK, &oldset, NULL);
352 // Return indicating success...
353 return 1;
355 #endif // !WIN32
358 #ifdef TEST
360 // Test code...
363 // Open the URI on the command-line...
364 int main(int argc, char **argv) {
365 char msg[1024];
368 if (argc != 2) {
369 puts("Usage: fl_open_uri URI");
370 return 1;
373 if (!fl_open_uri(argv[1], msg, sizeof(msg))) {
374 puts(msg);
375 return 1;
376 } else return 0;
378 #endif // TEST
382 // End of "$Id: fl_open_uri.cxx 8063 2010-12-19 21:20:10Z matt $".