Implement bash completion for gsettings
[platform/upstream/glib.git] / gio / gsettings-tool.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 2010 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but 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
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  * Author: Matthias Clasen
20  */
21
22 #include "config.h"
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <locale.h>
27 #include <gi18n.h>
28 #include <gio.h>
29
30 static gchar *
31 pick_word_at (const gchar  *s,
32               gint          cursor,
33               gint         *out_word_begins_at)
34 {
35   gint begin;
36   gint end;
37
38   if (s[0] == '\0')
39     {
40       if (out_word_begins_at != NULL)
41         *out_word_begins_at = -1;
42       return NULL;
43     }
44
45   if (g_ascii_isspace (s[cursor]) &&
46       ((cursor > 0 && g_ascii_isspace (s[cursor-1])) || cursor == 0))
47     {
48       if (out_word_begins_at != NULL)
49         *out_word_begins_at = cursor;
50       return g_strdup ("");
51     }
52   while (!g_ascii_isspace (s[cursor - 1]) && cursor > 0)
53     cursor--;
54   begin = cursor;
55
56   end = begin;
57   while (!g_ascii_isspace (s[end]) && s[end] != '\0')
58     end++;
59
60   if (out_word_begins_at != NULL)
61     *out_word_begins_at = begin;
62
63   return g_strndup (s + begin, end - begin);
64 }
65
66 static gint
67 usage (gint      *argc,
68        gchar    **argv[],
69        gboolean   use_stdout)
70 {
71   GOptionContext *context;
72   gchar *s;
73
74   g_set_prgname (g_path_get_basename ((*argv)[0]));
75
76   context = g_option_context_new (_("COMMAND"));
77   g_option_context_set_help_enabled (context, FALSE);
78   s = g_strdup_printf (
79     _("Commands:\n"
80       "  help        Show this information\n"
81       "  get         Get the value of a key\n"
82       "  set         Set the value of a key\n"
83       "  monitor     Monitor a key for changes\n"
84       "  writable    Check if a key is writable\n"
85       "\n"
86       "Use '%s COMMAND --help' to get help for individual commands.\n"),
87       g_get_prgname ());
88   g_option_context_set_description (context, s);
89   g_free (s);
90   s = g_option_context_get_help (context, FALSE, NULL);
91   if (use_stdout)
92     g_print ("%s", s);
93   else
94     g_printerr ("%s", s);
95   g_free (s);
96   g_option_context_free (context);
97
98   return use_stdout ? 0 : 1;
99 }
100
101 static void
102 remove_arg (gint num, gint *argc, gchar **argv[])
103 {
104   gint n;
105
106   g_assert (num <= (*argc));
107
108   for (n = num; (*argv)[n] != NULL; n++)
109     (*argv)[n] = (*argv)[n+1];
110   (*argv)[n] = NULL;
111   (*argc) = (*argc) - 1;
112 }
113
114
115 static void
116 modify_argv0_for_command (gint         *argc,
117                           gchar       **argv[],
118                           const gchar  *command)
119 {
120   gchar *s;
121
122   g_assert (g_strcmp0 ((*argv)[1], command) == 0);
123   remove_arg (1, argc, argv);
124
125   s = g_strdup_printf ("%s %s", (*argv)[0], command);
126   (*argv)[0] = s;
127 }
128
129 static gboolean
130 schema_exists (const gchar *name)
131 {
132   const gchar * const *schemas;
133   gint i;
134
135   schemas = g_settings_list_schemas ();
136   for (i = 0; schemas[i]; i++)
137     if (g_strcmp0 (name, schemas[i]) == 0)
138       return TRUE;
139
140   return FALSE;
141 }
142
143 static void
144 list_schemas (const gchar *prefix)
145 {
146   const gchar * const *schemas;
147   gint i;
148
149   schemas = g_settings_list_schemas ();
150   for (i = 0; schemas[i]; i++)
151     if (prefix == NULL || g_str_has_prefix (schemas[i], prefix))
152       g_print ("%s \n", schemas[i]);
153 }
154
155 static gboolean
156 key_exists (GSettings   *settings,
157             const gchar *name)
158 {
159   const gchar **keys;
160   gint i;
161   gboolean ret;
162
163   ret = FALSE;
164
165   keys = g_settings_list_keys (settings);
166   for (i = 0; keys[i]; i++)
167     if (g_strcmp0 (keys[i], name) == 0)
168       {
169         ret = TRUE;
170         break;
171       }
172   g_free (keys);
173
174   return ret;
175 }
176
177 static void
178 list_keys (GSettings   *settings,
179            const gchar *prefix)
180 {
181   const gchar **keys;
182   gint i;
183
184   keys = g_settings_list_keys (settings);
185   for (i = 0; keys[i]; i++)
186     if (prefix == NULL || g_str_has_prefix (keys[i], prefix))
187       g_print ("%s \n", keys[i]);
188   g_free (keys);
189 }
190
191 static void
192 list_options (GOptionContext *context,
193               const gchar    *prefix)
194 {
195   /* FIXME extract options from context */
196   const gchar *options[] = { "--help", "--path", NULL };
197   gint i;
198   for (i = 0; options[i]; i++)
199     if (g_str_has_prefix (options[i], prefix))
200       g_print ("%s \n", options[i]);
201 }
202
203 static gint
204 handle_get (gint      *argc,
205             gchar    **argv[],
206             gboolean   request_completion,
207             gchar     *completion_cur,
208             gchar     *completion_prev)
209 {
210   gchar *schema;
211   gchar *path;
212   gchar *key;
213   GSettings *settings;
214   GVariant *v;
215   GOptionContext *context;
216   GOptionEntry entries[] = {
217     { "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
218     { NULL }
219   };
220   GError *error;
221   gint ret = 1;
222
223   modify_argv0_for_command (argc, argv, "get");
224
225   context = g_option_context_new (_("SCHEMA KEY"));
226   g_option_context_set_help_enabled (context, FALSE);
227   g_option_context_set_summary (context, _("Get the value of KEY"));
228   g_option_context_set_description (context,
229     _("Arguments:\n"
230       "  SCHEMA      The id of the schema\n"
231       "  KEY         The name of the key\n"));
232   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
233
234   settings = NULL;
235   path = NULL;
236   schema = NULL;
237   key = NULL;
238
239   error = NULL;
240   if (!g_option_context_parse (context, argc, argv, NULL))
241     {
242       if (!request_completion)
243         {
244           gchar *s;
245           s = g_option_context_get_help (context, FALSE, NULL);
246           g_printerr ("%s", s);
247           g_free (s);
248
249           goto out;
250         }
251     }
252
253   if (*argc > 1)
254     schema = (*argv)[1];
255   if (*argc > 2)
256     key = (*argv)[2];
257
258   if (request_completion && completion_cur[0] == '-')
259     {
260       list_options (context, completion_cur);
261       ret = 0;
262       goto out;
263     }
264
265   if (request_completion && !schema_exists (schema))
266     {
267       list_schemas (schema);
268       ret = 0;
269       goto out;
270     }
271
272   if (path)
273     settings = g_settings_new_with_path (schema, path);
274   else
275     settings = g_settings_new (schema);
276
277   if (request_completion && !key_exists (settings, key))
278     {
279       list_keys (settings, key);
280       ret = 0;
281       goto out;
282     }
283
284   if (!request_completion)
285     {
286       v = g_settings_get_value (settings, key);
287       g_print ("%s\n", g_variant_print (v, FALSE));
288       g_variant_unref (v);
289       ret = 0;
290     }
291
292  out:
293   if (settings)
294     g_object_unref (settings);
295
296   g_option_context_free (context);
297
298   return ret;
299 }
300
301 static gint
302 handle_set (gint      *argc,
303             gchar    **argv[],
304             gboolean   request_completion,
305             gchar     *completion_cur,
306             gchar     *completion_prev)
307 {
308   gchar *schema;
309   gchar *path;
310   gchar *key;
311   gchar *value;
312   GSettings *settings;
313   GVariant *v, *default_v;
314   const GVariantType *type;
315   GOptionContext *context;
316   GOptionEntry entries[] = {
317     { "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
318     { NULL }
319   };
320   GError *error;
321   gint ret = 1;
322
323   modify_argv0_for_command (argc, argv, "set");
324
325   context = g_option_context_new (_("SCHEMA KEY VALUE"));
326   g_option_context_set_help_enabled (context, FALSE);
327   g_option_context_set_summary (context, _("Set the value of KEY"));
328   g_option_context_set_description (context,
329     _("Arguments:\n"
330       "  SCHEMA      The id of the schema\n"
331       "  KEY         The name of the key\n"
332       "  VALUE       The value to set key to, as a serialized GVariant\n"));
333   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
334
335   settings = NULL;
336   path = NULL;
337   schema = NULL;
338   key = NULL;
339
340   error = NULL;
341   if (!g_option_context_parse (context, argc, argv, NULL))
342     {
343       if (!request_completion)
344         {
345           gchar *s;
346           s = g_option_context_get_help (context, FALSE, NULL);
347           g_printerr ("%s", s);
348           g_free (s);
349           goto out;
350         }
351     }
352
353   if (*argc > 1)
354     schema = (*argv)[1];
355   if (*argc > 2)
356     key = (*argv)[2];
357   if (*argc > 3)
358     value = (*argv)[3];
359
360   if (request_completion && completion_cur[0] == '-')
361     {
362       list_options (context, completion_cur);
363       ret = 0;
364       goto out;
365     }
366
367   if (request_completion && !schema_exists (schema))
368     {
369       list_schemas (schema);
370       ret = 0;
371       goto out;
372     }
373
374   if (path)
375     settings = g_settings_new_with_path (schema, path);
376   else
377     settings = g_settings_new (schema);
378
379   if (request_completion && !key_exists (settings, key))
380     {
381       list_keys (settings, key);
382       ret = 0;
383       goto out;
384     }
385
386   if (!request_completion)
387     {
388       default_v = g_settings_get_value (settings, key);
389       type = g_variant_get_type (default_v);
390
391       error = NULL;
392       v = g_variant_parse (type, value, NULL, NULL, &error);
393       g_variant_unref (default_v);
394       if (v == NULL)
395         {
396           g_printerr ("%s\n", error->message);
397           goto out;
398         }
399
400       if (!g_settings_set_value (settings, key, v))
401         {
402           g_printerr (_("Key %s is not writable\n"), key);
403           goto out;
404         }
405
406       g_settings_sync ();
407       ret = 0;
408     }
409
410  out:
411   if (settings)
412     g_object_unref (settings);
413
414   g_option_context_free (context);
415
416   return ret;
417 }
418
419 static gint
420 handle_writable (gint   *argc,
421                  gchar **argv[],
422                  gboolean   request_completion,
423                  gchar     *completion_cur,
424                  gchar     *completion_prev)
425 {
426   gchar *schema;
427   gchar *path;
428   gchar *key;
429   GSettings *settings;
430   GOptionContext *context;
431   GOptionEntry entries[] = {
432     { "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
433     { NULL }
434   };
435   GError *error;
436   gint ret = 1;
437
438   modify_argv0_for_command (argc, argv, "writable");
439
440   context = g_option_context_new (_("SCHEMA KEY"));
441   g_option_context_set_help_enabled (context, FALSE);
442   g_option_context_set_summary (context, _("Find out whether KEY is writable"));
443   g_option_context_set_description (context,
444     _("Arguments:\n"
445       "  SCHEMA      The id of the schema\n"
446       "  KEY         The name of the key\n"));
447   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
448
449   settings = NULL;
450   path = NULL;
451   schema = NULL;
452   key = NULL;
453
454   error = NULL;
455   if (!g_option_context_parse (context, argc, argv, NULL))
456     {
457       if (!request_completion)
458         {
459           gchar *s;
460           s = g_option_context_get_help (context, FALSE, NULL);
461           g_printerr ("%s", s);
462           g_free (s);
463           goto out;
464         }
465     }
466
467   if (*argc > 1)
468     schema = (*argv)[1];
469   if (*argc > 2)
470     key = (*argv)[2];
471
472   if (request_completion && completion_cur[0] == '-')
473     {
474       list_options (context, completion_cur);
475       ret = 0;
476       goto out;
477     }
478
479   if (request_completion && !schema_exists (schema))
480     {
481       list_schemas (schema);
482       ret = 0;
483       goto out;
484     }
485
486   if (path)
487     settings = g_settings_new_with_path (schema, path);
488   else
489     settings = g_settings_new (schema);
490
491   if (request_completion && !key_exists (settings, key))
492     {
493       list_keys (settings, key);
494       ret = 0;
495       goto out;
496     }
497
498   if (!request_completion)
499     {
500       if (g_settings_is_writable (settings, key))
501         g_print ("true\n");
502       else
503         g_print ("false\n");
504       ret = 0;
505     }
506
507  out:
508   if (settings)
509     g_object_unref (settings);
510
511   g_option_context_free (context);
512
513   return ret;
514 }
515
516 static void
517 key_changed (GSettings   *settings,
518              const gchar *key)
519 {
520   GVariant *v;
521   gchar *value;
522
523   v = g_settings_get_value (settings, key);
524   value = g_variant_print (v, FALSE);
525   g_print ("%s\n", value);
526   g_free (value);
527   g_variant_unref (v);
528 }
529
530 static gint
531 handle_monitor (gint      *argc,
532                 gchar    **argv[],
533                 gboolean   request_completion,
534                 gchar     *completion_cur,
535                 gchar     *completion_prev)
536 {
537   gchar *schema;
538   gchar *path;
539   gchar *key;
540   GSettings *settings;
541   gchar *detailed_signal;
542   GMainLoop *loop;
543   GOptionContext *context;
544   GOptionEntry entries[] = {
545     { "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
546     { NULL }
547   };
548   GError *error;
549   gint ret = 1;
550
551   modify_argv0_for_command (argc, argv, "monitor");
552
553   context = g_option_context_new (_("SCHEMA KEY"));
554   g_option_context_set_help_enabled (context, FALSE);
555   g_option_context_set_summary (context,
556     _("Monitor KEY for changes and print the changed values.\n"
557       "Monitoring will continue until the process is terminated."));
558
559   g_option_context_set_description (context,
560     _("Arguments:\n"
561       "  SCHEMA      The id of the schema\n"
562       "  KEY         The name of the key\n"));
563   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
564
565   settings = NULL;
566   path = NULL;
567   schema = NULL;
568   key = NULL;
569
570   error = NULL;
571   if (!g_option_context_parse (context, argc, argv, NULL))
572     {
573       if (!request_completion)
574         {
575           gchar *s;
576           s = g_option_context_get_help (context, FALSE, NULL);
577           g_printerr ("%s", s);
578           g_free (s);
579           goto out;
580         }
581     }
582
583   if (*argc > 1)
584     schema = (*argv)[1];
585   if (*argc > 2)
586     key = (*argv)[2];
587
588   if (request_completion && completion_cur[0] == '-')
589     {
590       list_options (context, completion_cur);
591       ret = 0;
592       goto out;
593     }
594
595   if (request_completion && !schema_exists (schema))
596     {
597       list_schemas (schema);
598       ret = 0;
599       goto out;
600     }
601
602   if (path)
603     settings = g_settings_new_with_path (schema, path);
604   else
605     settings = g_settings_new (schema);
606
607   if (request_completion && !key_exists (settings, key))
608     {
609       list_keys (settings, key);
610       ret = 0;
611       goto out;
612     }
613
614   if (!request_completion)
615     {
616       detailed_signal = g_strdup_printf ("changed::%s", key);
617       g_signal_connect (settings, detailed_signal,
618                         G_CALLBACK (key_changed), NULL);
619
620       loop = g_main_loop_new (NULL, FALSE);
621       g_main_loop_run (loop);
622       g_main_loop_unref (loop);
623       ret = 0;
624     }
625
626  out:
627   if (settings)
628     g_object_unref (settings);
629
630   g_option_context_free (context);
631
632   return ret;
633 }
634 int
635 main (int argc, char *argv[])
636 {
637   gboolean ret;
638   gchar *command;
639   gboolean request_completion;
640   gchar *completion_cur;
641   gchar *completion_prev;
642
643   setlocale (LC_ALL, "");
644
645   g_type_init ();
646
647   ret = 1;
648   completion_cur = NULL;
649   completion_prev = NULL;
650   request_completion = FALSE;
651
652   if (argc < 2)
653     {
654       ret = usage (&argc, &argv, FALSE);
655       goto out;
656     }
657
658  again:
659   command = argv[1];
660
661   if (g_strcmp0 (command, "help") == 0)
662     {
663       if (!request_completion)
664         ret = usage (&argc, &argv, TRUE);
665     }
666   else if (g_strcmp0 (command, "get") == 0)
667     ret = handle_get (&argc, &argv, request_completion, completion_cur, completion_prev);
668   else if (g_strcmp0 (command, "set") == 0)
669     ret = handle_set (&argc, &argv, request_completion, completion_cur, completion_prev);
670   else if (g_strcmp0 (command, "monitor") == 0)
671     ret = handle_monitor (&argc, &argv, request_completion, completion_cur, completion_prev);
672   else if (g_strcmp0 (command, "writable") == 0)
673     ret = handle_writable (&argc, &argv, request_completion, completion_cur, completion_prev);
674   else if (g_strcmp0 (command, "complete") == 0 && argc == 4 && !request_completion)
675     {
676       gchar *completion_line;
677       gint completion_point;
678       gchar *endp;
679       gchar **completion_argv;
680       gint completion_argc;
681       gint cur_begin;
682
683       request_completion = TRUE;
684
685       completion_line = argv[2];
686       completion_point = strtol (argv[3], &endp, 10);
687       if (endp == argv[3] || *endp != '\0')
688         goto out;
689
690       if (!g_shell_parse_argv (completion_line,
691                                &completion_argc,
692                                &completion_argv,
693                                NULL))
694         {
695           /* can't parse partical cmdline, don't attempt completion */
696           goto out;
697         }
698
699       completion_prev = NULL;
700       completion_cur = pick_word_at (completion_line, completion_point, &cur_begin);
701       if (cur_begin > 0)
702         {
703           gint prev_end;
704           for (prev_end = cur_begin - 1; prev_end >= 0; prev_end--)
705             {
706               if (!g_ascii_isspace (completion_line[prev_end]))
707                 {
708                   completion_prev = pick_word_at (completion_line, prev_end, NULL);
709                   break;
710                 }
711             }
712         }
713
714       argc = completion_argc;
715       argv = completion_argv;
716
717       ret = 0;
718       goto again;
719     }
720   else
721     {
722       if (request_completion)
723         {
724           g_print ("help \nget \nmonitor \nwritable \nset \n");
725           ret = 0;
726         }
727       else
728         {
729           g_printerr (_("Unknown command '%s'\n"), argv[1]);
730           ret = usage (&argc, &argv, FALSE);
731         }
732     }
733
734  out:
735   g_free (completion_cur);
736   g_free (completion_prev);
737
738   return ret;
739 }