GApplication: support environment passing
[platform/upstream/glib.git] / gio / gapplicationcommandline.c
1 /*
2  * Copyright © 2010 Codethink Limited
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * licence or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
17  * USA.
18  *
19  * Authors: Ryan Lortie <desrt@desrt.ca>
20  */
21
22 #include "config.h"
23
24 #include "gapplicationcommandline.h"
25
26 #include "glibintl.h"
27
28 #include <string.h>
29 #include <stdio.h>
30
31 G_DEFINE_TYPE (GApplicationCommandLine, g_application_command_line, G_TYPE_OBJECT)
32
33 /**
34  * SECTION:gapplicationcommandline
35  * @title: GApplicationCommandLine
36  * @short_description: A class representing a command-line invocation of
37  *                     an application
38  * @see_also: #GApplication
39  *
40  * #GApplicationCommandLine represents a command-line invocation of
41  * an application.  It is created by #GApplication and emitted
42  * in the #GApplication::command-line signal and virtual function.
43  *
44  * The class contains the list of arguments that the program was invoked
45  * with.  It is also possible to query if the commandline invocation was
46  * local (ie: the current process is running in direct response to the
47  * invocation) or remote (ie: some other process forwarded the
48  * commandline to this process).
49  *
50  * The exit status of the originally-invoked process may be set and
51  * messages can be printed to stdout or stderr of that process.  The
52  * lifecycle of the originally-invoked process is tied to the lifecycle
53  * of this object (ie: the process exits when the last reference is
54  * dropped).
55  *
56  * <example id="gapplication-example-cmdline"><title>Handling commandline arguments with GApplication</title>
57  * <programlisting>
58  * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../gio/tests/gapplication-example-cmdline.c">
59  *   <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
60  * </xi:include>
61  * </programlisting>
62  * </example>
63  *
64  * <example id="gapplication-example-cmdline2"><title>Complicated commandline handling</title>
65  * <programlisting>
66  * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../gio/tests/gapplication-example-cmdline2.c">
67  *   <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
68  * </xi:include>
69  * </programlisting>
70  * </example>
71  **/
72
73 enum
74 {
75   PROP_NONE,
76   PROP_ARGUMENTS,
77   PROP_PLATFORM_DATA,
78   PROP_IS_REMOTE
79 };
80
81 struct _GApplicationCommandLinePrivate
82 {
83   GVariant *platform_data;
84   GVariant *arguments;
85   GVariant *cwd;
86
87   const gchar **environ;
88   gint exit_status;
89 };
90
91 /* All subclasses represent remote invocations of some kind. */
92 #define IS_REMOTE(cmdline) (G_TYPE_FROM_INSTANCE (cmdline) != \
93                             G_TYPE_APPLICATION_COMMAND_LINE)
94
95 static void
96 grok_platform_data (GApplicationCommandLine *cmdline)
97 {
98   GVariantIter iter;
99   const gchar *key;
100   GVariant *value;
101
102   g_variant_iter_init (&iter, cmdline->priv->platform_data);
103
104   while (g_variant_iter_loop (&iter, "{&sv}", &key, &value))
105     if (strcmp (key, "cwd") == 0)
106       {
107         if (!cmdline->priv->cwd)
108           cmdline->priv->cwd = g_variant_ref (value);
109       }
110
111     else if (strcmp (key, "environ") == 0)
112       {
113         if (!cmdline->priv->environ)
114           cmdline->priv->environ = g_variant_get_strv (value, NULL);
115       }
116 }
117
118 static void
119 g_application_command_line_real_print_literal (GApplicationCommandLine *cmdline,
120                                                const gchar             *message)
121 {
122   g_print ("%s\n", message);
123 }
124
125 static void
126 g_application_command_line_real_printerr_literal (GApplicationCommandLine *cmdline,
127                                                   const gchar             *message)
128 {
129   g_printerr ("%s\n", message);
130 }
131
132 static void
133 g_application_command_line_get_property (GObject    *object,
134                                          guint       prop_id,
135                                          GValue     *value,
136                                          GParamSpec *pspec)
137 {
138   GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
139
140   switch (prop_id)
141     {
142     case PROP_ARGUMENTS:
143       g_value_set_variant (value, cmdline->priv->arguments);
144       break;
145
146     case PROP_PLATFORM_DATA:
147       g_value_set_variant (value, cmdline->priv->platform_data);
148       break;
149
150     case PROP_IS_REMOTE:
151       g_value_set_boolean (value, IS_REMOTE (cmdline));
152       break;
153
154     default:
155       g_assert_not_reached ();
156     }
157 }
158
159 static void
160 g_application_command_line_set_property (GObject      *object,
161                                          guint         prop_id,
162                                          const GValue *value,
163                                          GParamSpec   *pspec)
164 {
165   GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
166
167   switch (prop_id)
168     {
169     case PROP_ARGUMENTS:
170       g_assert (cmdline->priv->arguments == NULL);
171       cmdline->priv->arguments = g_value_dup_variant (value);
172       break;
173
174     case PROP_PLATFORM_DATA:
175       g_assert (cmdline->priv->platform_data == NULL);
176       cmdline->priv->platform_data = g_value_dup_variant (value);
177       if (cmdline->priv->platform_data != NULL)
178         grok_platform_data (cmdline);
179       break;
180
181     default:
182       g_assert_not_reached ();
183     }
184 }
185
186 static void
187 g_application_command_line_finalize (GObject *object)
188 {
189   GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
190
191   if (cmdline->priv->platform_data)
192     g_variant_unref (cmdline->priv->platform_data);
193   if (cmdline->priv->arguments)
194     g_variant_unref (cmdline->priv->arguments);
195   if (cmdline->priv->cwd)
196     g_variant_unref (cmdline->priv->cwd);
197
198   G_OBJECT_CLASS (g_application_command_line_parent_class)
199     ->finalize (object);
200 }
201
202 static void
203 g_application_command_line_init (GApplicationCommandLine *cmdline)
204 {
205   cmdline->priv =
206     G_TYPE_INSTANCE_GET_PRIVATE (cmdline,
207                                  G_TYPE_APPLICATION_COMMAND_LINE,
208                                  GApplicationCommandLinePrivate);
209 }
210
211 static void
212 g_application_command_line_class_init (GApplicationCommandLineClass *class)
213 {
214   GObjectClass *object_class = G_OBJECT_CLASS (class);
215
216   object_class->get_property = g_application_command_line_get_property;
217   object_class->set_property = g_application_command_line_set_property;
218   object_class->finalize = g_application_command_line_finalize;
219   class->printerr_literal = g_application_command_line_real_printerr_literal;
220   class->print_literal = g_application_command_line_real_print_literal;
221
222   g_object_class_install_property (object_class, PROP_ARGUMENTS,
223     g_param_spec_variant ("arguments",
224                           P_("Commandline arguments"),
225                           P_("The commandline that caused this ::command-line signal emission"),
226                           G_VARIANT_TYPE_BYTESTRING_ARRAY, NULL,
227                           G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
228                           G_PARAM_STATIC_STRINGS));
229
230   g_object_class_install_property (object_class, PROP_PLATFORM_DATA,
231     g_param_spec_variant ("platform-data",
232                           P_("Platform data"),
233                           P_("Platform-specific data for the commandline"),
234                           G_VARIANT_TYPE ("a{sv}"), NULL,
235                           G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
236                           G_PARAM_STATIC_STRINGS));
237
238   g_object_class_install_property (object_class, PROP_IS_REMOTE,
239     g_param_spec_boolean ("is-remote",
240                           P_("Is remote"),
241                           P_("TRUE if this is a remote commandline"),
242                           FALSE,
243                           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
244
245   g_type_class_add_private (class, sizeof (GApplicationCommandLinePrivate));
246 }
247
248
249 /**
250  * g_application_command_line_get_arguments:
251  * @cmdline: a #GApplicationCommandLine
252  * @argc: the length of the arguments array, or %NULL
253  *
254  * Gets the list of arguments that was passed on the command line.
255  *
256  * The strings in the array may contain non-utf8 data.
257  *
258  * The return value is %NULL-terminated and should be freed using
259  * g_strfreev().
260  *
261  * Returns: the string array containing the arguments (the argv)
262  *
263  * Since: 2.28
264  **/
265 gchar **
266 g_application_command_line_get_arguments (GApplicationCommandLine *cmdline,
267                                           int                     *argc)
268 {
269   gchar **argv;
270   gsize len;
271
272   g_return_val_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline), NULL);
273
274   argv = g_variant_dup_bytestring_array (cmdline->priv->arguments, &len);
275
276   if (argc)
277     *argc = len;
278
279   return argv;
280 }
281
282 /**
283  * g_application_command_line_get_cwd:
284  * @cmdline: a #GApplicationCommandLine
285  *
286  * Gets the working directory of the command line invocation.  The
287  * string may contain non-utf8 data.
288  *
289  * It is possible that the remote application did not send a working
290  * directory, so this may be %NULL.
291  *
292  * The return value should not be modified or freed and is valid for as
293  * long as @cmdline exists.
294  *
295  * Returns: the current directory, or %NULL
296  *
297  * Since: 2.28
298  **/
299 const gchar *
300 g_application_command_line_get_cwd (GApplicationCommandLine *cmdline)
301 {
302   if (cmdline->priv->cwd)
303     return g_variant_get_bytestring (cmdline->priv->cwd);
304   else
305     return NULL;
306 }
307
308 /**
309  * g_application_command_line_get_environ:
310  * @cmdline: a #GApplicationCommandLine
311  *
312  * Gets the contents of the 'environ' variable of the command line
313  * invocation, as would be returned by g_get_environ().  The strings may
314  * contain non-utf8 data.
315  *
316  * The remote application usually does not send an environment.  Use
317  * %G_APPLICATION_SEND_ENVIRONMENT to affect that.  Even with this flag
318  * set it is possible that the environment is still not available (due
319  * to invocation messages from other applications).
320  *
321  * The return value should not be modified or freed and is valid for as
322  * long as @cmdline exists.
323  *
324  * Returns: the environment strings, or %NULL if they were not sent
325  * 
326  * Since: 2.28
327  **/
328 const gchar * const *
329 g_application_command_line_get_environ (GApplicationCommandLine *cmdline)
330 {
331   return cmdline->priv->environ;
332 }
333
334 /**
335  * g_application_command_line_getenv:
336  * @cmdline: a #GApplicationCommandLine
337  *
338  * Gets the value of a particular environment variable of the command
339  * line invocation, as would be returned by g_getenv().  The strings may
340  * contain non-utf8 data.
341  *
342  * The remote application usually does not send an environment.  Use
343  * %G_APPLICATION_SEND_ENVIRONMENT to affect that.  Even with this flag
344  * set it is possible that the environment is still not available (due
345  * to invocation messages from other applications).
346  *
347  * The return value should not be modified or freed and is valid for as
348  * long as @cmdline exists.
349  *
350  * Returns: the value of the variable, or %NULL if unset or unsent
351  * 
352  * Since: 2.28
353  **/
354 const gchar *
355 g_application_command_line_getenv (GApplicationCommandLine *cmdline,
356                                    const gchar             *name)
357 {
358   gint length = strlen (name);
359   gint i;
360
361   /* TODO: expand on windows */
362   if (cmdline->priv->environ)
363     for (i = 0; cmdline->priv->environ[i]; i++)
364       if (strncmp (cmdline->priv->environ[i], name, length) == 0 &&
365           cmdline->priv->environ[i][length] == '=')
366         return cmdline->priv->environ[i] + length + 1;
367
368   return NULL;
369 }
370
371 /**
372  * g_application_command_line_get_is_remote:
373  * @cmdline: a #GApplicationCommandLine
374  *
375  * Determines if @cmdline represents a remote invocation.
376  *
377  * Returns: %TRUE if the invocation was remote
378  *
379  * Since: 2.28
380  **/
381 gboolean
382 g_application_command_line_get_is_remote (GApplicationCommandLine *cmdline)
383 {
384   return IS_REMOTE (cmdline);
385 }
386
387 /**
388  * g_application_command_line_print:
389  * @cmdline: a #GApplicationCommandLine
390  * @format: a printf-style format string
391  * @...: arguments, as per @format
392  *
393  * Formats a message and prints it using the stdout print handler in the
394  * invoking process.
395  *
396  * If @cmdline is a local invocation then this is exactly equivalent to
397  * g_print().  If @cmdline is remote then this is equivalent to calling
398  * g_print() in the invoking process.
399  *
400  * Since: 2.28
401  **/
402 void
403 g_application_command_line_print (GApplicationCommandLine *cmdline,
404                                   const gchar             *format,
405                                   ...)
406 {
407   gchar *message;
408   va_list ap;
409
410   g_return_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline));
411   g_return_if_fail (format != NULL);
412
413   va_start (ap, format);
414   message = g_strdup_vprintf (format, ap);
415   va_end (ap);
416
417   G_APPLICATION_COMMAND_LINE_GET_CLASS (cmdline)
418     ->print_literal (cmdline, message);
419   g_free (message);
420 }
421
422 /**
423  * g_application_command_line_printerr:
424  * @cmdline: a #GApplicationCommandLine
425  * @format: a printf-style format string
426  * @...: arguments, as per @format
427  *
428  * Formats a message and prints it using the stderr print handler in the
429  * invoking process.
430  *
431  * If @cmdline is a local invocation then this is exactly equivalent to
432  * g_printerr().  If @cmdline is remote then this is equivalent to
433  * calling g_printerr() in the invoking process.
434  *
435  * Since: 2.28
436  **/
437 void
438 g_application_command_line_printerr (GApplicationCommandLine *cmdline,
439                                      const gchar             *format,
440                                      ...)
441 {
442   gchar *message;
443   va_list ap;
444
445   g_return_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline));
446   g_return_if_fail (format != NULL);
447
448   va_start (ap, format);
449   message = g_strdup_vprintf (format, ap);
450   va_end (ap);
451
452   G_APPLICATION_COMMAND_LINE_GET_CLASS (cmdline)
453     ->printerr_literal (cmdline, message);
454   g_free (message);
455 }
456
457 /**
458  * g_application_command_line_set_exit_status:
459  * @cmdline: a #GApplicationCommandLine
460  * @exit_status: the exit status
461  *
462  * Sets the exit status that will be used when the invoking process
463  * exits.
464  *
465  * The return value of the #GApplication::command-line signal is
466  * passed to this function when the handler returns.  This is the usual
467  * way of setting the exit status.
468  *
469  * In the event that you want the remote invocation to continue running
470  * and want to decide on the exit status in the future, you can use this
471  * call.  For the case of a remote invocation, the remote process will
472  * typically exit when the last reference is dropped on @cmdline.  The
473  * exit status of the remote process will be equal to the last value
474  * that was set with this function.
475  *
476  * In the case that the commandline invocation is local, the situation
477  * is slightly more complicated.  If the commandline invocation results
478  * in the mainloop running (ie: because the use-count of the application
479  * increased to a non-zero value) then the application is considered to
480  * have been 'successful' in a certain sense, and the exit status is
481  * always zero.  If the application use count is zero, though, the exit
482  * status of the local #GApplicationCommandLine is used.
483  *
484  * Since: 2.28
485  **/
486 void
487 g_application_command_line_set_exit_status (GApplicationCommandLine *cmdline,
488                                             int                      exit_status)
489 {
490   g_return_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline));
491
492   cmdline->priv->exit_status = exit_status;
493 }
494
495 /**
496  * g_application_command_line_get_exit_status:
497  * @cmdline: a #GApplicationCommandLine
498  *
499  * Gets the exit status of @cmdline.  See
500  * g_application_command_line_set_exit_status() for more information.
501  *
502  * Returns: the exit status
503  *
504  * Since: 2.28
505  **/
506 int
507 g_application_command_line_get_exit_status (GApplicationCommandLine *cmdline)
508 {
509   g_return_val_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline), -1);
510
511   return cmdline->priv->exit_status;
512 }
513
514 /**
515  * g_application_command_line_get_platform_data:
516  * @cmdline: #GApplicationCommandLine
517  *
518  * Gets the platform data associated with the invocation of @cmdline.
519  *
520  * This is a #GVariant dictionary containing information about the
521  * context in which the invocation occured.  It typically contains
522  * information like the current working directory and the startup
523  * notification ID.
524  *
525  * For local invocation, it will be %NULL.
526  *
527  * Returns: the platform data, or %NULL
528  *
529  * Since: 2.28
530  **/
531 GVariant *
532 g_application_command_line_get_platform_data (GApplicationCommandLine *cmdline)
533 {
534   g_return_val_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline), NULL);
535
536   if (cmdline->priv->platform_data)
537     return g_variant_ref (cmdline->priv->platform_data);
538   else
539       return NULL;
540 }