release 1.3.4
[cygwin-run.git] / src / util.c
blobacfc4a554117a8c280718dfb4e3dac8d007bcdc9
1 /*
2 * Copyright (c) 2006,2009 Charles S. Wilson
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
23 #if HAVE_CONFIG_H
24 # include <config.h>
25 #endif
27 #if STDC_HEADERS
28 # include <stdlib.h>
29 # include <stdarg.h>
30 # include <string.h>
31 # include <float.h>
32 #endif
34 #include <stdio.h>
35 #include <errno.h>
36 #include <assert.h>
37 #include <ctype.h>
39 #if HAVE_SYS_TYPES_H
40 # include <sys/types.h>
41 #endif
42 #if HAVE_MALLOC_H
43 # include <malloc.h>
44 #endif
45 #if HAVE_SYS_STAT_H
46 # include <sys/stat.h>
47 #endif
48 #if HAVE_PWD_H
49 # include <pwd.h>
50 #endif
51 #if HAVE_UNISTD_H
52 # include <unistd.h>
53 #endif
54 #if HAVE_SYS_CYGWIN_H
55 # include <sys/cygwin.h>
56 #endif
57 #if HAVE_WINDOWS_H && HAVE_OPENCLIPBOARD
58 # define WIN32_LEAD_AND_MEAN
59 # define NOMINMAX
60 # include <windows.h>
61 #endif
63 #ifndef ORIGINAL_RUN
64 #include <ustr.h>
65 #endif
66 #include "util.h"
68 char *program_name = NULL;
70 static int gui_mode = 1; /* enabled */
71 static int tty_mode = 1; /* enabled */
72 static int verbose_level = 0; /* only FATAL errors, by default */
74 int run2_gui_is_allowed(void) { return gui_mode; }
75 int run2_tty_is_allowed(void) { return tty_mode; }
76 int run2_get_verbose_level(void) { return verbose_level; }
78 void run2_set_gui_mode(int mode) { gui_mode = mode; }
79 void run2_set_tty_mode(int mode) { tty_mode = mode; }
80 void run2_set_verbose_level(int level){ verbose_level = level;}
82 const char *
83 run2_get_program_name (void)
85 if (!program_name || !*program_name)
86 return "run2";
87 return program_name;
90 void
91 run2_set_program_name (const char *p)
93 if (program_name)
94 free (program_name);
95 program_name = run2_strdup (run2_basename (p));
98 const char *
99 run2_basename (const char *name)
101 const char *base;
103 #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(_MSC_VER)
104 /* Skip over the disk name in MSDOS pathnames. */
105 if (isalpha ((unsigned char) name[0]) && name[1] == ':')
106 name += 2;
107 #endif
109 for (base = name; *name; name++)
110 if ((*name == '/')
111 #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(_MSC_VER)
112 || (*name == '\\')
113 #endif
115 base = name + 1;
116 return base;
120 run2_ends_with(const char* s1, const char* s2)
122 int len1;
123 int len2;
124 int retval = 0;
125 len1 = strlen(s1);
126 len2 = strlen(s2);
127 if (len1 - len2 >= 0)
128 if (strcasecmp(&(s1[len1-len2]),s2) == 0)
129 retval = 1;
130 return retval;
133 void
134 run2_strip_exe(char* s)
136 size_t slen = strlen(s);
137 if ((slen > 4) && /* long enough to have .exe extension */
138 /* second part not evaluated (short circuit) if exec_arg too short */
139 (strcasecmp(&(s[slen-4]),".exe") == 0))
140 s[slen-4] = '\0';
143 #ifndef ORIGINAL_RUN
145 * This function attempts to locate a file which may be in any of
146 * several directories. Unlike the original pfopen, it does not
147 * return a FILE pointer to the opened file, but rather returns
148 * the fully-qualified filename of the first match found. Returns
149 * NULL if not found.
151 Ustr*
152 run2_pfopen (const Ustr *name, const Ustr *dirs)
154 Ustr *tdirs = NULL;
155 Ustr *tname = NULL;
156 Ustr *returnVal = NULL;
157 Ustr *tok = USTR_NULL;
158 size_t off = 0;
160 int foundit = 0;
162 if (dirs == NULL || ustr_len (dirs) == 0)
163 return NULL;
165 tname = USTR_CHECK (ustr_dup (name));
166 tdirs = USTR_CHECK (ustr_dup (dirs));
167 while ((tok = ustr_split_cstr (tdirs, &off, SEP_CHARS, USTR_NULL, USTR_FLAG_SPLIT_DEF)) &&
168 (foundit == 0))
170 foundit = run2_fileExists (&returnVal, tok, tname);
171 ustr_free(tok); /* have to free to avoid mem leaks */
174 ustr_free(tdirs);
175 ustr_free(tname);
176 return returnVal;
180 run2_fileExists (Ustr **fullname, const Ustr* path, const Ustr* name)
182 int retval = 0;
183 FILE* file;
184 size_t len;
185 Ustr* worku = NULL;
186 char* workc = NULL;
187 if (path != NULL)
189 worku = ustr_dup (path);
190 len = ustr_len (worku);
191 workc = ustr_wstr (worku);
192 if (len && workc[len-1] != '/' && workc[len-1] != '\\')
194 USTR_ADD_OSTR (&worku, PATH_SEP_CHAR_STR);
197 else
198 worku = ustr_dup_empty ();
200 ustr_add (&worku, name);
202 workc = (char *) cygwin_create_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, ustr_cstr (worku));
203 if (!workc)
205 errorMsg ("Unable to convert \"%s\" to posix format: %s",
206 ustr_cstr (worku), strerror (errno));
208 else
210 debugMsg (3, "(%s) looking for...\t%s", __func__, workc);
212 file = fopen (workc, "rb");
213 if (file != NULL)
215 fclose(file);
216 if (fullname != NULL)
217 *fullname = USTR_CHECK (ustr_dup_cstr (workc));
218 retval = TRUE;
220 free (workc);
222 ustr_free (worku);
223 return retval;
227 Ustr *
228 ustr_check (Ustr * s, const char * fn, int ln)
230 if (!s)
232 run2_error (EXIT_FAILURE, ENOMEM,
233 "Unable to allocate memory for ustr: %s:%d",
234 fn, ln);
235 abort ();
237 if ((ustr_ro (s) == USTR_FALSE) &&
238 (ustr_enomem (s) == USTR_TRUE))
240 run2_error (EXIT_FAILURE, ENOMEM,
241 "Unable to allocate memory for ustr: %s:%d",
242 fn, ln);
243 abort ();
245 return s;
247 #else /* ORIGINAL_RUN */
249 * This function attempts to locate a file which may be in any of
250 * several directories. Unlike the original pfopen, it does not
251 * return a FILE pointer to the opened file, but rather returns
252 * the fully-qualified filename of the first match found. Returns
253 * NULL if not found.
255 char*
256 run2_pfopen (const char *name, const char *dirs)
258 char *tdirs = NULL;
259 char *tname = NULL;
260 char *returnVal = NULL;
261 char *tok = NULL;
262 size_t off = 0;
264 int foundit = 0;
266 if (dirs == NULL || *dirs == '\0')
267 return NULL;
269 tname = run2_strdup (name);
270 tdirs = run2_strdup (dirs);
271 for (tok = strtok (tdirs, SEP_CHARS);
272 (foundit == 0) && (tok != NULL);
273 tok = strtok (NULL, SEP_CHARS))
275 foundit = run2_fileExists (&returnVal, tok, tname);
278 free(tdirs);
279 free(tname);
280 return returnVal;
284 run2_fileExists (char **fullname, const char* path, const char* name)
286 int retval = 0;
287 FILE* file;
288 size_t len;
289 char* worku = NULL;
290 char* workc = NULL;
291 if (path != NULL)
293 worku = run2_strdup (path);
294 len = strlen (worku);
295 if (len && worku[len-1] != '/' && worku[len-1] != '\\')
297 workc = run2_extend_str(worku, PATH_SEP_CHAR_STR, 1);
298 worku = workc;
301 else
302 worku = run2_strdup("");
304 workc = run2_extend_str(worku, name, 1);
305 worku = workc;
307 #ifdef __CYGWIN__
308 workc = (char *) cygwin_create_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, worku);
309 if (!workc)
311 errorMsg ("Unable to convert \"%s\" to posix format: %s",
312 worku, strerror (errno));
314 else
316 #endif
317 debugMsg (3, "(%s) looking for...\t%s", __func__, workc);
319 file = fopen (workc, "rb");
320 if (file != NULL)
322 fclose(file);
323 if (fullname != NULL)
324 *fullname = run2_strdup (workc);
325 retval = TRUE;
327 #ifdef __CYGWIN__
328 /* on non-cygwin, worku and workc point to the same location */
329 free (workc);
331 #endif
332 free (worku);
333 return retval;
335 #endif /* ORIGINAL_RUN */
338 void run2_message_(int level, const char* fmt, ...)
340 va_list args;
341 va_start(args, fmt);
342 run2_vmessage_(level, fmt, args);
343 va_end(args);
346 void run2_vmessage_(int level, const char* fmt, va_list args)
348 char buf[10000];
349 char titlebuf[200];
350 int i,j;
351 const char *pn = program_name;
352 if (!pn || !*pn)
353 pn = "run2";
355 j = vsprintf(buf,fmt,args);
356 buf[j] = '\0'; /* paranoia */
357 if (ENABLE_TTY && (run2_tty_is_allowed()))
359 if (level >= RUN2_LOG_DEBUG)
360 fprintf(stderr, "%s DEBUG: %s\n", pn, buf);
361 else if (level < RUN2_LOG_FATAL)
362 fprintf (stdout, buf);
363 else
364 switch (level)
366 case RUN2_LOG_FATAL: fprintf(stderr, "%s FATAL: %s\n", pn, buf); break;
367 case RUN2_LOG_ERROR: fprintf(stderr, "%s Error: %s\n", pn, buf); break;
368 case RUN2_LOG_WARN: fprintf(stderr, "%s Warning: %s\n", pn, buf); break;
369 case RUN2_LOG_INFO: fprintf(stderr, "%s Info: %s\n", pn, buf); break;
370 default: fprintf(stderr, "%s <unknown criticality>: %s\n", pn, buf); break;
372 return;
374 if (ENABLE_GUI && (run2_gui_is_allowed()))
376 i = 0;
377 if (level >= RUN2_LOG_DEBUG)
378 i = sprintf(titlebuf, "%s DEBUG", pn);
379 else if (level < RUN2_LOG_FATAL)
380 i = sprintf(titlebuf, "%s", pn);
381 else
382 switch (level)
384 case RUN2_LOG_FATAL: i = sprintf(titlebuf, "%s FATAL", pn); break;
385 case RUN2_LOG_ERROR: i = sprintf(titlebuf, "%s Error", pn); break;
386 case RUN2_LOG_WARN: i = sprintf(titlebuf, "%s Warning", pn); break;
387 case RUN2_LOG_INFO: i = sprintf(titlebuf, "%s Info", pn); break;
388 default: i = sprintf(titlebuf, "%s <unknown criticality>", pn); break;
390 titlebuf[i] = '\0'; /* paranoia */
392 if (level >= RUN2_LOG_DEBUG)
393 MessageBox(NULL, buf, titlebuf, MB_ICONINFORMATION);
394 else if (level < RUN2_LOG_FATAL)
395 MessageBox(NULL, buf, titlebuf, MB_ICONINFORMATION);
396 else
397 switch (level)
399 case RUN2_LOG_FATAL: MessageBox(NULL, buf, titlebuf, MB_ICONERROR); break;
400 case RUN2_LOG_ERROR: MessageBox(NULL, buf, titlebuf, MB_ICONERROR); break;
401 case RUN2_LOG_WARN: MessageBox(NULL, buf, titlebuf, MB_ICONWARNING); break;
402 case RUN2_LOG_INFO: MessageBox(NULL, buf, titlebuf, MB_ICONINFORMATION); break;
403 default: MessageBox(NULL, buf, titlebuf, MB_ICONERROR); break;
405 return;
409 char**
410 run2_dupargv (char **vector)
412 int argc;
413 char **rval;
415 if (vector == NULL)
416 return NULL;
418 for (argc = 0; vector[argc] != NULL; argc++)
420 rval = (char **) run2_malloc ((argc + 1) * sizeof (char *));
422 for (argc = 0; vector[argc] != NULL; argc++)
424 int len = strlen (vector[argc]);
425 rval[argc] = run2_strdup (vector[argc]);
427 rval[argc] = NULL;
428 return rval;
432 run2_countargv (char **vector)
434 char **scan;
435 int rc = 0;
436 if (vector != NULL)
437 for (scan = vector; *scan != NULL; scan++)
438 rc++;
439 return rc;
442 void
443 run2_freeargv (char **vector)
445 char **scan;
446 if (vector != NULL)
448 for (scan = vector; *scan != NULL; scan++)
450 free (*scan);
451 *scan = NULL;
453 free (vector);
457 static void
458 run2_verror (int status, int errnum, const char *message, va_list args)
460 if (errnum)
462 /* grow the message to hold error info */
463 char const *s = strerror (errnum);
464 char *newmsg;
465 if (!s) s = "Unknown system error";
466 newmsg = run2_extend_str (message, ": ", 1);
467 newmsg = run2_extend_str (newmsg, s, 1);
468 verrorMsg (newmsg, args);
470 else
472 verrorMsg (message, args);
474 if (status)
475 exit (status);
478 void
479 run2_error (int status, int errnum, const char *message, ...)
481 va_list args;
482 va_start (args, message);
483 run2_verror (status, errnum, message, args);
484 va_end (args);
487 void
488 run2_malloc_exit (void)
490 run2_error (EXIT_FAILURE, 0, "Unable to allocate memory");
491 abort ();
494 void*
495 run2_malloc (size_t sz)
497 void *p = malloc (sz);
498 if (!p && sz != 0)
499 run2_malloc_exit ();
500 return p;
503 void*
504 run2_realloc (void *p, size_t sz)
506 p = realloc (p, sz);
507 if (!p && sz != 0)
508 run2_malloc_exit ();
509 return p;
512 char*
513 run2_strdup (const char *s)
515 size_t len = strlen (s);
516 char * d = (char *) run2_malloc (len + 1);
518 if (s)
519 strncpy (d, s, len + 1);
520 else
521 d[0] = '\0';
523 return d;
526 char*
527 run2_quote_strdup (const char *s, int quote)
529 size_t len = strlen (s);
530 if (len == 0)
531 return "\"\"";
532 if (quote && (len > strcspn (s, " \t\v\"\\")))
534 size_t i, j = 0;
535 char * d = (char *) run2_malloc (2*len + 3);
536 d[j++] = '"';
537 for (i=0; i<len; i++)
539 switch (s[i]) {
540 case '\\':
541 d[j++] = '\\';
542 d[j++] = '\\';
543 break;
544 case '"':
545 d[j++] = '\\';
546 d[j++] = '"';
547 break;
548 default:
549 d[j++] = s[i];
552 d[j++] = '"';
553 d[j++] = '\0';
554 return d;
556 return run2_strdup (s);
559 char *
560 run2_extend_str (const char *orig_value, const char *add, int to_end)
562 char *new_value;
563 if (orig_value && *orig_value)
565 int orig_value_len = strlen (orig_value);
566 int add_len = strlen (add);
567 new_value = (char *) run2_malloc (add_len + orig_value_len + 1);
568 if (to_end)
570 strcpy (new_value, orig_value);
571 strcpy (new_value + orig_value_len, add);
573 else
575 strcpy (new_value, add);
576 strcpy (new_value + add_len, orig_value);
579 else
581 new_value = run2_strdup (add);
583 return new_value;
587 run2_strtol(char *arg, long *value)
589 char *endptr;
590 int errno_save = errno;
592 assert(arg!=NULL);
593 errno = 0;
594 *value = strtol(arg, &endptr, 0);
595 if (errno != 0 || *endptr!='\0' || endptr==arg) {
596 return 1;
598 errno = errno_save;
599 return 0;
602 #if HAVE_PWD_H
603 char *
604 run2_get_homedir (const char *user)
606 char *home;
607 struct passwd *pwd;
609 if (!user || !*user)
611 home = getenv("HOME");
612 if (home && *home)
613 return run2_strdup (home);
614 errno = 0;
615 pwd = getpwuid (getuid());
616 if (pwd)
617 return run2_strdup (pwd->pw_dir);
618 errorMsg ("Could not determine home directory for current user");
619 return NULL;
622 errno = 0;
623 pwd = getpwnam (user);
624 if (pwd)
625 return run2_strdup (pwd->pw_dir);
626 errorMsg ("Could not determine home directory for user %s", user);
627 return NULL;
629 #else
630 char *
631 run2_get_homedir (const char *user)
633 char *home;
635 if (!user || !*user)
637 home = getenv("HOME");
638 if (home && *home)
639 return run2_strdup (home);
640 errorMsg ("Could not determine home directory for current user");
641 return NULL;
644 errorMsg ("Could not determine home directory for user %s", user);
645 return NULL;
647 #endif