1 /* $Id$ */
2 /*-
3  * Copyright (c) 2003-2006 Benedikt Meurer <benny@xfce.org>
4  * All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free
18  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301 USA
20  */
21 
22 /**
23  * SECTION: xfce-miscutils
24  * @title: Miscellaneous Utilities
25  * @short_description: miscellaneous file-related utility functions.
26  *
27  * Miscellaneous file-related utility functions.
28  */
29 
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33 
34 #ifdef HAVE_SYS_TYPES_H
35 #include <sys/types.h>
36 #endif
37 #ifdef HAVE_SYS_STAT_H
38 #include <sys/stat.h>
39 #endif
40 #ifdef HAVE_SYS_UTSNAME_H
41 #include <sys/utsname.h>
42 #endif
43 
44 #ifdef HAVE_ERR_H
45 #include <err.h>
46 #endif
47 #include <errno.h>
48 #ifdef HAVE_MEMORY_H
49 #include <memory.h>
50 #endif
51 #ifdef HAVE_PWD_H
52 #include <pwd.h>
53 #endif
54 #ifdef HAVE_STDARG_H
55 #include <stdarg.h>
56 #elif defined (HAVE_VARARGS_H)
57 #include <varargs.h>
58 #endif
59 #include <stdio.h>
60 #ifdef HAVE_STDLIB_H
61 #include <stdlib.h>
62 #endif
63 #ifdef HAVE_STRING_H
64 #include <string.h>
65 #endif
66 #ifdef HAVE_UNISTD_H
67 #include <unistd.h>
68 #endif
69 
70 #include <gio/gio.h>
71 
72 #include <libxfce4util/libxfce4util.h>
73 #include <libxfce4util/libxfce4util-alias.h>
74 
75 #define XFCE4DIR ".xfce4"
76 
77 /* environment variable the user can set to change the path to
78  * the users .xfce4 directory. If not set, the xfce_userdir defaults
79  * to "$HOME/.xfce4".
80  */
81 #define XFCE4HOME_ENVVAR "XFCE4HOME"
82 
83 G_LOCK_DEFINE_STATIC(_lock);
84 
85 
86 static gchar *xfce_homedir = NULL; /* path to users home directory */
87 static gchar *xfce_userdir = NULL; /* path to users .xfce4 directory */
88 
89 
90 
91 static void
internal_initialize(void)92 internal_initialize(void)
93 {
94   const gchar *dir;
95 
96   /* determine path to users home directory */
97   dir = g_get_home_dir ();
98   if (dir == NULL)
99     {
100 #ifdef HAVE_ERR_H
101       errx (EXIT_FAILURE, "Unable to determine users home directory");
102 #else
103       fprintf (stderr, "%s: ", g_get_prgname ());
104       fprintf (stderr, "Unable to determinte users home directory");
105       fprintf (stderr, "\n");
106       exit (EXIT_FAILURE);
107 #endif
108     }
109   else
110     {
111       xfce_homedir = g_strdup (dir);
112     }
113 
114   /* get path to users .xfce4 directory */
115   dir = g_getenv (XFCE4HOME_ENVVAR);
116   if (dir != NULL)
117     {
118       xfce_userdir = g_strdup (dir);
119     }
120   else
121     {
122       xfce_userdir = g_build_filename (xfce_homedir, XFCE4DIR, NULL);
123     }
124 }
125 
126 
127 
128 static gchar*
internal_get_file_r(const gchar * dir,gchar * buffer,gsize len,const gchar * format,va_list ap)129 internal_get_file_r (const gchar *dir,
130                      gchar       *buffer,
131                      gsize        len,
132                      const gchar *format,
133                      va_list      ap)
134 {
135   gsize n;
136 
137   g_return_val_if_fail(buffer != NULL, NULL);
138   g_return_val_if_fail(format != NULL, NULL);
139   g_return_val_if_fail(len > 0, NULL);
140 
141   if ((n = g_strlcpy(buffer, dir, len)) >= len)
142     return NULL;
143 
144   if ((n = g_strlcat(buffer, G_DIR_SEPARATOR_S, len)) >= len)
145     return NULL;
146 
147   if ((gsize) g_vsnprintf(buffer + n, len - n, format, ap) >= len - n)
148     return NULL;
149 
150   return buffer;
151 }
152 
153 
154 
155 /**
156  * xfce_version_string:
157  *
158  * Queries the version string of the installed Xfce desktop environment.
159  *
160  * Return value: the overall version information of the installed Xfce desktop.
161  *
162  * Since: 4.2
163  */
164 const gchar*
xfce_version_string(void)165 xfce_version_string (void)
166 {
167   return XFCE_VERSION_STRING;
168 }
169 
170 
171 
172 /**
173  * xfce_get_homedir:
174  *
175  * Similar to g_get_homedir() in functionality but will never return NULL.
176  * While g_get_homedir() may return NULL under certain circumstances, this
177  * function is garantied to never ever return NULL, but always return a
178  * valid character pointer with the absolute path to the user's home directory.
179  *
180  * The returned string is owned by libxfce4util and must not be freed by
181  * the caller.
182  *
183  * Return value: the path to the current user's home directory.
184  **/
185 const gchar*
xfce_get_homedir(void)186 xfce_get_homedir (void)
187 {
188   G_LOCK (_lock);
189   if (!xfce_homedir)
190     internal_initialize ();
191   G_UNLOCK (_lock);
192 
193   return xfce_homedir;
194 }
195 
196 
197 
198 /**
199  * xfce_get_homefile_r:
200  * @buffer  : pointer to a user provided destination buffer.
201  * @length  : size of @buffer in bytes.
202  * @format  : printf style format string.
203  * @...     : the arguments to substitute in the output.
204  *
205  * Similar in functionality to #xfce_get_homefile, but uses a user
206  * defined @buffer instead of allocating a new buffer.
207  *
208  * xfce_get_homefile_r uses safe string operations, that says, it garanties
209  * that the resulting string is always zero terminated, as long as the
210  * @length is greater than zero.
211  *
212  * Return value: pointer to @buffer.
213  **/
214 gchar*
xfce_get_homefile_r(gchar * buffer,size_t len,const gchar * format,...)215 xfce_get_homefile_r (gchar *buffer, size_t len, const gchar *format, ...)
216 {
217   gchar  *ptr;
218   va_list ap;
219 
220   va_start (ap, format);
221   ptr = internal_get_file_r (xfce_get_homedir (), buffer, len, format, ap);
222   va_end (ap);
223 
224   return ptr;
225 }
226 
227 
228 
229 /**
230  * xfce_get_userdir:
231  *
232  * Safe way to retrieve the path to the user's ".xfce4" directory. The path
233  * to the current user's ".xfce4" directory is either taken from the
234  * environment variable XFCE4HOME if defined, or if unset, is gained by
235  * concatenating the path to the user's home directory and the ".xfce4".
236  * That says, it will, by default, return the path "$HOME/.xfce4", where
237  * $HOME is replaced with the absolute path to the user's home directory.
238  *
239  * The returned string is managed by libxfce4util and must not be freed by
240  * the caller.
241  *
242  * Return value: the path to the current user's ".xfce4" directory.
243  */
244 const gchar*
xfce_get_userdir(void)245 xfce_get_userdir (void)
246 {
247   G_LOCK(_lock);
248   if (!xfce_userdir)
249     {
250       internal_initialize();
251 
252       /* verify that the directory exists or is created */
253       if (!g_file_test (xfce_userdir, G_FILE_TEST_IS_DIR))
254         xfce_mkdirhier (xfce_userdir, 0700, NULL);
255     }
256   G_UNLOCK(_lock);
257 
258   return xfce_userdir;
259 }
260 
261 
262 
263 /**
264  * xfce_get_userfile_r:
265  * @buffer  : user provided destination buffer.
266  * @length  : size of @buffer in bytes.
267  * @format  : printf style format string.
268  * @...     : arguments to substitute in the output.
269  *
270  * Return value: pointer to @buffer.
271  **/
272 gchar*
xfce_get_userfile_r(gchar * buffer,size_t length,const gchar * format,...)273 xfce_get_userfile_r (gchar *buffer, size_t length, const gchar *format, ...)
274 {
275   gchar  *ptr;
276   va_list ap;
277 
278   va_start (ap, format);
279   ptr = internal_get_file_r (xfce_get_userdir (), buffer, length, format, ap);
280   va_end (ap);
281 
282   return ptr;
283 }
284 
285 
286 
287 /**
288  * xfce_gethostname:
289  *
290  * Portable way to query the hostname of the node running the process. This
291  * function does not ever return %NULL, but always returns a string containing
292  * the current node's hostname.
293  *
294  * Return value: the current node's hostname. The string has to be freed
295  *               by the caller using g_free().
296  **/
297 gchar*
xfce_gethostname(void)298 xfce_gethostname (void)
299 {
300 #if defined(HAVE_GETHOSTNAME)
301 #ifndef MAXHOSTNAMELEN
302 #define MAXHOSTNAMELEN 256
303 #endif
304   char hostname[MAXHOSTNAMELEN];
305 
306   if (gethostname (hostname, MAXHOSTNAMELEN) == 0)
307     return g_strdup (hostname);
308 #elif defined(HAVE_SYS_UTSNAME_H)
309   struct utsname name;
310 
311   if (uname (&name) == 0)
312     return g_strdup (name.nodename);
313 #endif
314   g_error ("Unable to determine your hostname: %s", g_strerror (errno));
315   /* NOT REACHED */
316   return NULL;
317 }
318 
319 
320 
321 static inline gboolean
xfce_is_valid_tilde_prefix(const gchar * p)322 xfce_is_valid_tilde_prefix (const gchar *p)
323 {
324   if (g_ascii_isspace (*p) /* thunar ~/music */
325       || *p == '=' /* terminal --working-directory=~/ */
326       || *p == '\'' || *p == '"') /* terminal --working-directory '~my music' */
327     return TRUE;
328 
329   return FALSE;
330 }
331 
332 
333 
334 /**
335  * xfce_expand_variables:
336  * @command : Input string or %NULL.
337  * @envp    : Addition environment variables to take into account. These
338  *            variables have higher priority than the ones in the process's
339  *            environment.
340  *
341  * Expands shell like environment variables and tilde (~/ and ~user/ are both supported)
342  * in @command.
343  *
344  * Return value: %NULL on error, else the string, which should be freed using
345  *               g_free() when no longer needed.
346  *
347  * Since: 4.2
348  **/
349 gchar *
xfce_expand_variables(const gchar * command,gchar ** envp)350 xfce_expand_variables (const gchar *command,
351                        gchar      **envp)
352 {
353   GString        *buf;
354   const gchar    *start;
355   gchar          *variable;
356   const gchar    *p;
357   const gchar    *value;
358   gchar         **ep;
359   guint           len;
360 #ifdef HAVE_GETPWNAM
361   struct passwd  *pw;
362   gchar          *username;
363 #endif
364 
365   if (G_UNLIKELY (command == NULL))
366     return NULL;
367 
368   buf = g_string_sized_new (strlen (command));
369 
370   for (p = command; *p != '\0'; ++p)
371     {
372       continue_without_increase:
373 
374       if (*p == '~'
375           && (p == command
376               || xfce_is_valid_tilde_prefix (p - 1)))
377         {
378           /* walk to the end of the string or to a directory separator */
379           for (start = ++p; *p != '\0' && *p != G_DIR_SEPARATOR; ++p);
380 
381           if (G_LIKELY (start == p))
382             {
383               /* add the current user directory */
384               buf = g_string_append (buf, xfce_get_homedir ());
385             }
386           else
387             {
388 #ifdef HAVE_GETPWNAM
389               username = g_strndup (start, p - start);
390               pw = getpwnam (username);
391               g_free (username);
392 
393               /* add the users' home directory if found, fallback to the
394                * not-expanded string */
395               if (pw != NULL && pw->pw_dir != NULL)
396                 buf = g_string_append (buf, pw->pw_dir);
397               else
398 #endif
399                 buf = g_string_append_len (buf, start - 1, p - start + 1);
400             }
401 
402           /* we are either at the end of the string or *p is a separator,
403            * so continue to add it to the result buffer */
404         }
405       else if (*p == '$')
406         {
407           /* walk to the end of a valid variable name */
408           for (start = ++p; *p != '\0' && (g_ascii_isalnum (*p) || *p == '_'); ++p);
409 
410           if (start < p)
411             {
412               value = NULL;
413               len = p - start;
414 
415               /* lookup the variable in the environment supplied by the user */
416               if (envp != NULL)
417                 {
418                   /* format is NAME=VALUE */
419                   for (ep = envp; *ep != NULL; ++ep)
420                     if (strncmp (*ep, start, len) == 0
421                         && (*ep)[len] == '=')
422                       {
423                         value = (*ep) + len + 1;
424                         break;
425                       }
426                 }
427 
428               /* fallback to the environment */
429               if (value == NULL)
430                 {
431                   variable = g_strndup (start, len);
432                   value = g_getenv (variable);
433                   g_free (variable);
434                 }
435 
436               if (G_LIKELY (value != NULL))
437                 {
438                   buf = g_string_append (buf, value);
439                 }
440               else
441                 {
442                   /* the variable name was valid, but no value was
443                    * found, insert nothing and continue */
444                 }
445 
446               /* *p is at the start of the charater after the variable,
447                * so continue scanning without advancing the string offset
448                * so two variables are replaced properly */
449               goto continue_without_increase;
450             }
451           else
452             {
453               /* invalid variable format, add the
454                * $ character and continue */
455               --p;
456             }
457         }
458 
459       buf = g_string_append_c (buf, *p);
460     }
461 
462   return g_string_free (buf, FALSE);
463 }
464 
465 
466 
467 void
xfce_append_quoted(GString * string,const gchar * unquoted)468 xfce_append_quoted (GString     *string,
469                     const gchar *unquoted)
470 {
471   gchar *quoted;
472 
473   quoted = g_shell_quote (unquoted);
474   g_string_append (string, quoted);
475   g_free (quoted);
476 }
477 
478 
479 
480 /**
481  * xfce_expand_desktop_entry_field_codes:
482  * @command           : Input string (command to expand) or %NULL.
483  * @uri_list          : Input string list (filename/URL field) or %NULL.
484  * @icon              : Input string (icon field) or %NULL.
485  * @name              : Input string (name field) or %NULL.
486  * @uri               : Input string (URI field) or %NULL.
487  * @requires_terminal : Input boolean.
488  *
489  * Expands field codes in @command according to Freedesktop.org Desktop Entry Specification.
490  *
491  * Return value: %NULL on error, else the string, which should be freed using g_free() when
492  *               no longer needed.
493  **/
494 gchar*
xfce_expand_desktop_entry_field_codes(const gchar * command,GSList * uri_list,const gchar * icon,const gchar * name,const gchar * uri,gboolean requires_terminal)495 xfce_expand_desktop_entry_field_codes (const gchar *command,
496                                        GSList      *uri_list,
497                                        const gchar *icon,
498                                        const gchar *name,
499                                        const gchar *uri,
500                                        gboolean     requires_terminal)
501 {
502   const gchar *p;
503   gchar       *filename;
504   GString     *string;
505   GSList      *li;
506   GFile       *file;
507 
508   if (G_UNLIKELY (command == NULL))
509     return NULL;
510 
511   string = g_string_sized_new (strlen (command));
512 
513   if (requires_terminal)
514     g_string_append (string, "exo-open --launch TerminalEmulator ");
515 
516   for (p = command; *p != '\0'; ++p)
517     {
518       if (G_UNLIKELY (p[0] == '%' && p[1] != '\0'))
519         {
520           switch (*++p)
521             {
522             case 'f':
523             case 'F':
524               for (li = uri_list; li != NULL; li = li->next)
525                 {
526                   /* passing through a GFile seems necessary to properly handle
527                    * all URI schemes, in particular g_filename_from_uri() is not
528                    * able to do so */
529                   file = g_file_new_for_uri (li->data);
530                   filename = g_file_get_path (file);
531                   if (G_LIKELY (filename != NULL))
532                     xfce_append_quoted (string, filename);
533 
534                   g_object_unref (file);
535                   g_free (filename);
536 
537                   if (*p == 'f')
538                     break;
539                   if (li->next != NULL)
540                     g_string_append_c (string, ' ');
541                 }
542               break;
543 
544             case 'u':
545             case 'U':
546               for (li = uri_list; li != NULL; li = li->next)
547                 {
548                   xfce_append_quoted (string, li->data);
549 
550                   if (*p == 'u')
551                     break;
552                   if (li->next != NULL)
553                     g_string_append_c (string, ' ');
554                 }
555               break;
556 
557             case 'i':
558               if (! xfce_str_is_empty (icon))
559                 {
560                   g_string_append (string, "--icon ");
561                   xfce_append_quoted (string, icon);
562                 }
563               break;
564 
565             case 'c':
566               if (! xfce_str_is_empty (name))
567                 xfce_append_quoted (string, name);
568               break;
569 
570             case 'k':
571               if (! xfce_str_is_empty (uri))
572                 xfce_append_quoted (string, uri);
573               break;
574 
575             case '%':
576               g_string_append_c (string, '%');
577               break;
578             }
579         }
580       else
581         {
582           g_string_append_c (string, *p);
583         }
584     }
585 
586   return g_string_free (string, FALSE);
587 }
588 
589 
590 
591 #define __XFCE_MISCUTILS_C__
592 #include <libxfce4util/libxfce4util-aliasdef.c>
593