1 /*
2  *  misc.c:		Miscellaneous functions
3  *
4  *  Written by:		Ullrich Hafner
5  *
6  *  Copyright (C) 1998 Ullrich Hafner <hafner@bigfoot.de>
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
21  */
22 
23 /*
24  *  $Date: 2000/09/25 18:15:23 $
25  *  $Author: hafner $
26  *  $Revision: 1.28 $
27  *  $State: Exp $
28  */
29 
30 #include "config.h"
31 
32 #if HAVE_STDLIB_H
33 #	include <stdlib.h>
34 #endif /* not HAVE_STDLIB_H */
35 #if HAVE_STRING_H
36 #	include <string.h>
37 #else /* not HAVE_STRING_H */
38 #	include <strings.h>
39 #endif /* not HAVE_STRING_H */
40 #if HAVE_SYS_TYPES_H
41 #	include <sys/types.h>
42 #endif /* HAVE_SYS_TYPES_H */
43 #ifdef HAVE_UNISTD_H
44 #	include <unistd.h>
45 #endif /* HAVE_UNISTD_H */
46 #include <stdio.h>
47 #include <errno.h>
48 #include <gtk/gtk.h>
49 #include <pwd.h>
50 #if HAVE_SYS_STAT_H
51 #	include <sys/stat.h>
52 #endif /* HAVE_SYS_STAT_H */
53 /* unistd.h defines _POSIX_VERSION on POSIX.1 systems. */
54 #if defined(HAVE_DIRENT_H) || defined(_POSIX_VERSION)
55 #   include <dirent.h>
56 #   define NLENGTH(dirent) (strlen ((dirent)->d_name))
57 #else
58 #   define dirent direct
59 #   define NLENGTH(dirent) ((dirent)->d_namlen)
60 #   ifdef HAVE_SYS_NDIR_H
61 #       include <sys/ndir.h>
62 #   endif /* HAVE_SYS_NDIR_H */
63 #   ifdef HAVE_SYS_DIR_H
64 #       include <sys/dir.h>
65 #   endif /* HAVE_SYS_DIR_H */
66 #   ifdef HAVE_NDIR_H
67 #       include <ndir.h>
68 #   endif /* HAVE_NDIR_H */
69 #endif /* not (HAVE_DIRENT_H or _POSIX_VERSION) */
70 
71 #include "misc.h"
72 #include "load.h"
73 #include "dialog.h"
74 #include "error.h"
75 
76 void
Free(void * ptr)77 Free (void *ptr)
78 {
79    g_free (ptr);
80 }
81 
82 void *
Calloc(size_t n,size_t size)83 Calloc (size_t n, size_t size)
84 /*
85  *  Allocate memory like calloc ().
86  *
87  *  Return value: Pointer to the new block of memory on success,
88  *		  else terminate the program.
89  */
90 {
91    void		*ptr;			/* pointer to the new memory block */
92 
93    if (n <= 0 || size <= 0)
94       error ("Can't allocate memory for %d items of size %d",
95 	     (unsigned) n, (unsigned) size);
96 
97    ptr = g_malloc (n * size);
98    if (ptr == NULL)
99       error (_("Out of memory!"));
100    memset (ptr, 0, n * size);
101    return ptr;
102 }
103 
104 char *
get_tempdir(void)105 get_tempdir (void)
106 {
107    static char * tempdir;
108    tempdir = getenv ("TMPDIR");
109    if (tempdir && *tempdir)
110       return g_strdup (tempdir);
111    else
112       return g_strdup ("/tmp");
113 }
114 
115 char *
make_temporary_directory(void)116 make_temporary_directory (void)
117 {
118 #ifdef HAVE_MKDTEMP
119    static char *name = NULL;
120    static char *tmpdir;
121 
122    Free (name);
123    tmpdir = get_tempdir ();
124    name = g_strconcat (tmpdir, "/wmdirXXXXXX", NULL);
125    Free (tmpdir);
126 
127    if (!mkdtemp (name))
128    {
129       Free (name);
130       name = NULL;
131    }
132 
133 #else /* not HAVE_MKDTEMP */
134    char *name = tmpnam (NULL);
135    if (name)
136       make_directory (name);
137 
138 #endif /* not HAVE_MKDTEMP */
139 
140    if (name)
141       return name;
142    else
143    {
144       make_directory ("/tmp/wmakerconf.dir");
145       return "/tmp/wmakerconf.dir";
146    }
147 }
148 
149 char *
get_temporary_file_name(void)150 get_temporary_file_name (void)
151 {
152 #ifdef HAVE_MKSTEMP
153    static char *name = NULL;
154    static char *tmpdir;
155    int fd;
156 
157    Free (name);
158    tmpdir = get_tempdir ();
159    name = g_strconcat (tmpdir, "/wmcnfXXXXXX", NULL);
160    Free (tmpdir);
161 
162    if ((fd = mkstemp (name)) >= 0)
163       close (fd);
164    else
165    {
166       Free (name);
167       name = NULL;
168    }
169 
170 #else  /* not HAVE_MKSTEMP */
171    char *name = tmpnam (NULL);
172 #endif /* not HAVE_MKSTEMP */
173 
174    if (name)
175       return name;
176    else
177       return "/tmp/wmakerconf.tmp";
178 }
179 
180 char *
get_gnustep_path(const char * domain)181 get_gnustep_path (const char *domain)
182 /*
183  *  Generate path to file 'domain' in users GNUstep directory
184  *
185  *  E.g. domain = 'Defaults/WindowMaker', 'Defaults/WMRootMenu'
186  *
187  *  Return value:
188  *	pointer to char array containing generated path
189  */
190 {
191    const char *gspath = getenv ("GNUSTEP_USER_ROOT");
192 
193    if (gspath)
194       return g_strconcat (gspath, "/", domain, NULL);
195    else
196       return g_strconcat (g_get_home_dir (), "/GNUstep/", domain, NULL);
197 }
198 
199 char *
expand_tilde(const char * name)200 expand_tilde (const char *name)
201 /*
202  *  Try to expand tilde (~) in filename.
203  *
204  *  Return value:
205  *	string with expanded path
206  */
207 {
208    struct passwd *user = NULL;
209 
210    assert (name);
211 
212    if (name [0] != '~' && name [0] != '$')
213       return g_strdup (name);		/* nothing to do */
214 
215    if (name [1] == '/' || !name [1]	/* users home directory */
216        || strncmp (name, "$HOME", 5) == 0
217        || strncmp (name, "$(HOME)", 7) == 0)
218    {
219       if (name [0] == '~')
220 	 name += 1;			/* skip ~ */
221       else if (strncmp (name, "$HOME", 5) == 0)
222 	 name += 5;
223       else if (strncmp (name, "$(HOME)", 7) == 0)
224 	 name += 7;
225 
226       return g_strconcat (g_get_home_dir (), name, NULL);
227    }
228    else if (name [0] == '$')		/* environment expansion */
229    {
230       const char *first = name + 1;
231       const char *last;
232       const char *rest;
233 
234       if (*first == '(')		/* $(ENV) */
235       {
236 	 if (!(last = strrchr (name, ')')))
237 	    return g_strdup (name);	/* parse error */
238 	 first++;
239 	 rest = last + 1;
240 	 last--;
241       }
242       else
243       {
244 	 if (!(rest = strchr (name, '/'))) /* "$ENV" */
245 	 {
246 	    last = first + strlen (first) - 1;
247 	    rest = last + 1;		/* empty */
248 	 }
249 	 else				/* "$ENV/rest" */
250 	    last = rest - 1;
251       }
252       {
253 	 char		*var = g_strndup (first, last - first + 1);
254 	 const char	*dir = getenv (var);
255 
256 	 Free (var);
257 	 if (dir)
258 	    return g_strconcat (dir, rest, NULL);
259 	 else
260 	    return g_strdup (name);	/* parse error */
261       }
262    }
263 #ifdef HAVE_GETPWNAM
264    else					/* other user directory */
265    {
266       char *usrname = strchr (name, '/');
267 
268       if (usrname)
269       {
270 	 char *tmp = g_strndup (name + 1, usrname - name - 1);
271 
272 	 user = getpwnam (tmp);
273 	 Free (tmp);
274 	 name = usrname;		/* first slash */
275       }
276       else
277       {
278 	 user = getpwnam (name + 1);
279 	 name += strlen (name);		/* empty string */
280       }
281    }
282 
283    if (!user)
284       warning (_("Can't find passwd entry to expand\n`~' in filename %s"));
285    else
286       return g_strconcat (user->pw_dir, name, NULL);
287 #endif /* HAVE_GETPWNAM */
288 
289    return g_strdup (name);		/* no success */
290 }
291 
292 bool_t
delete_file_or_dir(const char * filename)293 delete_file_or_dir (const char *filename)
294 /*
295  *  Delete the given file or directory 'filename' (works like rm and rmdir).
296  *  If the operation fails, show a popup error window.
297  *
298  *  Return value:
299  *	FALSE on success, TRUE on error
300  */
301 {
302 #ifdef HAVE_REMOVE
303 
304    if (remove (filename) == -1)
305    {
306       dialog_popup (DIALOG_ERROR, NULL, NULL,
307 		    _("Can't delete\n'%s'\n\n%s"),
308 		    filename, strerror (errno));
309       return YES;
310    }
311    else
312       return NO;
313 
314 #else  /* not HAVE_REMOVE */
315 
316    char   *tmp;
317    DIR    *dir 	      = opendir (filename);
318    char   *quotedname = protect_quotes (g_strdup (filename));
319    char   *msg 	      = g_strdup_printf (_("Can't delete\n`%s'"), filename);
320    bool_t  returncode;
321 
322    if (!dir)				/* a file */
323       tmp = g_strconcat ("rm -f \"", quotedname, "\"", NULL);
324    else					/* a directory */
325    {
326       tmp = g_strconcat ("rmdir \"", quotedname, "\"", NULL);
327       closedir (dir);
328    }
329 
330    returncode = shell_command (tmp, msg);
331 
332    g_free (tmp);
333    g_free (msg);
334    g_free (quotedname);
335 
336    return returncode;
337 
338 #endif /* not HAVE_REMOVE */
339 }
340 
341 bool_t
rename_file_or_dir(const char * oldname,const char * newname)342 rename_file_or_dir (const char *oldname, const char *newname)
343 /*
344  *  Rename file 'oldname' to 'newname' (works like mv).
345  *  If the operation fails, show a popup error window.
346  *
347  *  Return value:
348  *	FALSE on success, TRUE on error
349  */
350 {
351 #ifdef HAVE_RENAME
352 
353    if (rename (oldname, newname) == -1)
354    {
355       dialog_popup (DIALOG_ERROR, NULL, NULL,
356 		    _("Can't move\n'%s'\nto\n'%s'\n\n%s"),
357 		    oldname, newname, strerror (errno));
358       return YES;
359    }
360    else
361       return NO;
362 
363 #else  /* not HAVE_RENAME */
364 
365    char   *quotedoldname = protect_quotes (g_strdup (oldname));
366    char   *quotednewname = protect_quotes (g_strdup (newname));
367    char   *cmd 		 = g_strconcat ("mv \"", quotedoldname, "\" \"",
368 					quotednewname, "\"", NULL);
369    char   *msg 		 = g_strdup_printf (_("Can't move\n'%s'\nto\n'%s'"),
370 					    oldname, newname);
371    bool_t  returncode;
372 
373    returncode = shell_command (cmd, msg);
374 
375    g_free (cmd);
376    g_free (msg);
377    g_free (quotednewname);
378    g_free (quotedoldname);
379 
380    return returncode;
381 
382 #endif /* not HAVE_RENAME */
383 }
384 
385 bool_t
remove_directory(const char * directory)386 remove_directory (const char *directory)
387 /*
388  *  Recursively delete the given directory 'directory' with all its
389  *  files and subdirectories (works like rm -rf).
390  *  If the operation fails, show a popup error window.
391  *
392  *  Return value:
393  *	FALSE on success, TRUE on error
394  */
395 {
396    DIR *dir = opendir (directory);
397 
398    if (!dir)				/* a file */
399       return delete_file_or_dir (directory);
400    else
401    {
402       struct dirent *file;
403 
404       while ((file = readdir (dir)))
405       {
406 	 if (!streq (file->d_name, ".")
407 	     && !streq (file->d_name, ".."))
408 	 {
409 	    char *name = g_strconcat (directory, "/", file->d_name, NULL);
410 	    if (remove_directory (name))
411 	    {
412 	       closedir (dir);
413 	       Free (name);
414 	       return YES;
415 	    };
416 	    Free (name);
417 	 }
418       }
419       closedir (dir);
420       return delete_file_or_dir (directory);
421    }
422 }
423 
424 bool_t
make_directory(const char * dirname)425 make_directory (const char *dirname)
426 /*
427  *  Create a nmew directory 'dirname' (works like mkdir).
428  *  If the operation fails, show a popup error window.
429  *
430  *  Return value:
431  *	FALSE on success, TRUE on error
432  */
433 {
434 #if defined(HAVE_MKDIR) && defined(HAVE_SYS_STAT_H)
435 
436    if (mkdir (dirname, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ) == -1)
437    {
438       dialog_popup (DIALOG_ERROR, NULL, NULL,
439 		    _("Can't create directory\n'%s'\n\n%s"),
440 		    dirname, strerror (errno));
441       return YES;
442    }
443    else
444       return NO;
445 
446 
447 #else  /* not defined(HAVE_MKDIR) && defined(HAVE_SYS_STAT_H) */
448 
449    char *quotedname = protect_quotes (g_strdup (dirname));
450    char *cmd 	    = g_strconcat ("mkdir \"", quotedname, "\"", NULL);
451    char *msg 	    = g_strdup_printf (_("Can't create directory\n`%s'\n"),
452 				       quotedname);
453 
454    returncode = shell_command (cmd, msg);
455 
456    g_free (cmd);
457    g_free (msg);
458    g_free (quotedname);
459 
460    return returncode;
461 
462 #endif /* not defined(HAVE_MKDIR) && defined(HAVE_SYS_STAT_H) */
463 }
464 
465 bool_t
copy_file(const char * dst,const char * src)466 copy_file (const char *dst, const char *src)
467 /*
468  *  Copy file 'src' to file or directory 'dst' (works like cp).
469  *  If the operation fails, show a popup error window.
470  *
471  *  Return value:
472  *	FALSE on success, TRUE on error
473  */
474 {
475    if (!streq (src, dst))
476    {
477       DIR  *dir = opendir (dst);
478       char *dst_name;
479       FILE *fsrc;
480       FILE *fdst;
481 
482       if (dir)				/* directory is destination */
483       {
484 	 closedir (dir);
485 	 if (streq (dst, g_path_get_dirname (src)))
486 	    return 0;
487 	 dst_name = g_strconcat (dst, "/", g_path_get_basename (src), NULL);
488       }
489       else
490 	 dst_name = g_strdup (dst);
491 
492       fsrc = fopen (src, "r");
493       if (!fsrc)
494       {
495 	 dialog_popup (DIALOG_ERROR, NULL, NULL,
496 		       _("Can't open input file\n'%s'\n\n%s"),
497 		       src, strerror (errno));
498 	 return YES;
499       }
500       fdst = fopen (dst_name, "w");
501       if (!fdst)
502       {
503 	 fclose (fsrc);
504 	 dialog_popup (DIALOG_ERROR, NULL, NULL,
505 		       _("Can't open output file\n'%s'\n\n%s"),
506 		       dst_name, strerror (errno));
507 	 return YES;
508       }
509       {
510 	 char   buffer [4096];
511 	 size_t n;
512 
513 	 do
514 	 {
515 	    size_t m;
516 
517 	    n = fread (buffer, 1, 4096, fsrc);
518 	    if (n)
519 	       m = fwrite (buffer, 1, n, fdst);
520 	    else
521 	       m = 0;
522 	    if (n != m)
523 	    {
524 	       fclose (fsrc);
525 	       fclose (fdst);
526 	       dialog_popup (DIALOG_ERROR, NULL, NULL,
527 			     _("Can't write to output file\n'%s'\n\n%s"),
528 			     dst_name, strerror (errno));
529 	       return YES;
530 	    }
531 	 } while (n == 4096);
532       }
533       fclose (fsrc);
534       fclose (fdst);
535       Free (dst_name);
536    }
537    return NO;
538 }
539 
540 bool_t
shell_command(const char * command,const char * text)541 shell_command (const char *command, const char *text)
542 /*
543  *  Execute shell 'command'.
544  *  If the operation fails, show a popup error window.
545  *
546  *  Return value:
547  *	FALSE on success, TRUE on error
548  */
549 {
550 #if defined(HAVE_POPEN)
551    extern GtkWidget *log_text;
552    GtkTextBuffer    *buffer;
553    GtkTextIter	     iter;
554    char 	    *cmd  = g_strconcat (command, " 2>&1", NULL);
555    FILE		    *pipe = popen (cmd, "r");
556    char		     tmp [MAXSTRLEN];
557 
558    Free (cmd);
559    if (!pipe)
560    {
561       if (text)
562 	 dialog_popup (DIALOG_ERROR, NULL, NULL,
563 		       _("%s\nShell command failed.\n'%s'"), text, command);
564       else
565 	 dialog_popup (DIALOG_ERROR, NULL, NULL,
566 		       _("Shell command failed.\n'%s'"), command);
567       return YES;
568    }
569 
570    buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (log_text));
571    while (fgets (tmp, MAXSTRLEN, pipe))
572    {
573       gtk_text_buffer_get_iter_at_offset (buffer, &iter, -1);
574       gtk_text_buffer_insert (buffer, &iter, tmp, -1);
575 
576       gtk_widget_show_all (gtk_widget_get_toplevel (log_text));
577       while (gtk_events_pending())
578 	 gtk_main_iteration();
579    }
580    if (pclose (pipe))
581    {
582       if (text)
583 	 dialog_popup (DIALOG_ERROR, NULL, NULL,
584 		       _("%s\nShell command failed.\n'%s'"), text, command);
585       else
586 	 dialog_popup (DIALOG_ERROR, NULL, NULL,
587 		       _("Shell command failed.\n'%s'"), command);
588       return YES;
589    }
590    else
591       return NO;
592 
593 #else /* not HAVE_POPEN */
594 
595    if (system (command))
596    {
597       if (text)
598 	 dialog_popup (DIALOG_ERROR, NULL, NULL,
599 		       _("%s\nShell command failed.\n'%s'\n"
600 			 "Please check `stderr' for more details."),
601 		       text, command);
602       else
603 	 dialog_popup (DIALOG_ERROR, NULL, NULL,
604 		       _("Shell command failed.\n'%s'\n"
605 			 "Please check `stderr' for more details."), command);
606       return YES;
607    }
608    else
609       return NO;
610 
611 #endif /* not HAVE_POPEN */
612 }
613 
614 bool_t
file_exists(const char * filename)615 file_exists (const char *filename)
616 /*
617  *  Checks whether a directory or file 'filename' already exists.
618  *
619  *  Return value:
620  *	FALSE if file does not exist, TRUE if file exists
621  */
622 {
623    DIR *dir = opendir (filename);
624 
625    if (dir)				/* directory exists */
626    {
627       closedir (dir);
628       return YES;
629    }
630    else
631    {
632       FILE *file = fopen (filename, "r");
633       if (!file)
634 	 return NO;
635       else
636       {
637 	 fclose (file);
638 	 return YES;
639       }
640    }
641 }
642 
643 char *
preview_name(const char * name)644 preview_name (const char *name)
645 /*
646  *  Compute the filename of the preview of the given image 'name'.
647  *  E.g., filename /path/to/foo.jpg is expanded to
648  *  ~/.wmakerconf-path-to-foo.jpg
649  *
650  *  Return value:
651  *	preview filename
652  */
653 {
654    char *str = g_strdup (name);
655    char *ptr, *path;
656 
657    for (ptr = str; *ptr; ptr++)
658       if (*ptr == '/')
659 	 *ptr = '-';
660    path = g_strconcat (g_get_home_dir (), "/.wmakerconf/", str, NULL);
661    Free (str);
662 
663    return path;
664 }
665 
666 proplist_t
read_proplist(const char * filename)667 read_proplist (const char *filename)
668 /*
669  *  Call Alfredo's PropList parser ...
670  */
671 {
672    DIR *dir = opendir (filename);
673 
674    if (dir)				/* directory */
675    {
676       closedir (dir);
677       return NULL;
678    }
679    else
680       return ReadProplistFromFile (filename);
681 }
682 
683 char *
protect_quotes(char * string)684 protect_quotes (char *string)
685 /*
686  *  Protect double quotes in the given string.
687  *  The old string is freed if it contains a double quote ".
688  *
689  *  Return value:
690  *	new string with \" instead of "
691  */
692 {
693    char *pos;
694    int  offset = 0;
695 
696    while ((pos = strchr (string + offset, '"')))
697    {
698       char *new;
699       if (pos > string + offset)	/* not first character */
700       {
701 	 if (*(pos - 1) == '\\')	/* if '\' is already present */
702 	 {
703 	    offset = pos - string + 1;
704 	    continue;
705 	 }
706       }
707       offset = pos - string + 2;
708 
709       *pos = 0;
710       new = g_strconcat (string, "\\\"", pos + 1, NULL);
711       Free (string);
712 
713       string = new;
714    }
715 
716    return string;
717 }
718