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