Cleanup the GSettingsBackend API
[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       "  reset       Reset the value of a key\n"
84       "  monitor     Monitor a key for changes\n"
85       "  writable    Check if a key is writable\n"
86       "\n"
87       "Use '%s COMMAND --help' to get help for individual commands.\n"),
88       g_get_prgname ());
89   g_option_context_set_description (context, s);
90   g_free (s);
91   s = g_option_context_get_help (context, FALSE, NULL);
92   if (use_stdout)
93     g_print ("%s", s);
94   else
95     g_printerr ("%s", s);
96   g_free (s);
97   g_option_context_free (context);
98
99   return use_stdout ? 0 : 1;
100 }
101
102 static void
103 remove_arg (gint num, gint *argc, gchar **argv[])
104 {
105   gint n;
106
107   g_assert (num <= (*argc));
108
109   for (n = num; (*argv)[n] != NULL; n++)
110     (*argv)[n] = (*argv)[n+1];
111   (*argv)[n] = NULL;
112   (*argc) = (*argc) - 1;
113 }
114
115
116 static void
117 modify_argv0_for_command (gint         *argc,
118                           gchar       **argv[],
119                           const gchar  *command)
120 {
121   gchar *s;
122
123   g_assert (g_strcmp0 ((*argv)[1], command) == 0);
124   remove_arg (1, argc, argv);
125
126   s = g_strdup_printf ("%s %s", (*argv)[0], command);
127   (*argv)[0] = s;
128 }
129
130 static gboolean
131 schema_exists (const gchar *name)
132 {
133   const gchar * const *schemas;
134   gint i;
135
136   schemas = g_settings_list_schemas ();
137   for (i = 0; schemas[i]; i++)
138     if (g_strcmp0 (name, schemas[i]) == 0)
139       return TRUE;
140
141   return FALSE;
142 }
143
144 static void
145 list_schemas (const gchar *prefix)
146 {
147   const gchar * const *schemas;
148   gint i;
149
150   schemas = g_settings_list_schemas ();
151   for (i = 0; schemas[i]; i++)
152     if (prefix == NULL || g_str_has_prefix (schemas[i], prefix))
153       g_print ("%s \n", schemas[i]);
154 }
155
156 static gboolean
157 key_exists (GSettings   *settings,
158             const gchar *name)
159 {
160   const gchar **keys;
161   gint i;
162   gboolean ret;
163
164   ret = FALSE;
165
166   keys = g_settings_list_items (settings);
167   for (i = 0; keys[i]; i++)
168     if (!g_str_has_suffix (keys[i], "/") &&
169         g_strcmp0 (keys[i], name) == 0)
170       {
171         ret = TRUE;
172         break;
173       }
174   g_free (keys);
175
176   return ret;
177 }
178
179 static void
180 list_keys (GSettings   *settings,
181            const gchar *prefix)
182 {
183   const gchar **keys;
184   gint i;
185
186   keys = g_settings_list_items (settings);
187   for (i = 0; keys[i]; i++)
188     {
189       if (!g_str_has_suffix (keys[i], "/") &&
190           (prefix == NULL || g_str_has_prefix (keys[i], prefix)))
191         g_print ("%s \n", keys[i]);
192     }
193   g_free (keys);
194 }
195
196 static void
197 list_options (GOptionContext *context,
198               const gchar    *prefix)
199 {
200   /* FIXME extract options from context */
201   const gchar *options[] = { "--help", "--path", NULL };
202   gint i;
203   for (i = 0; options[i]; i++)
204     if (g_str_has_prefix (options[i], prefix))
205       g_print ("%s \n", options[i]);
206 }
207
208 static gint
209 handle_get (gint      *argc,
210             gchar    **argv[],
211             gboolean   request_completion,
212             gchar     *completion_cur,
213             gchar     *completion_prev)
214 {
215   gchar *schema;
216   gchar *path;
217   gchar *key;
218   GSettings *settings;
219   GVariant *v;
220   GOptionContext *context;
221   GOptionEntry entries[] = {
222     { "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
223     { NULL }
224   };
225   GError *error;
226   gint ret = 1;
227
228   modify_argv0_for_command (argc, argv, "get");
229
230   context = g_option_context_new (_("SCHEMA KEY"));
231   g_option_context_set_help_enabled (context, FALSE);
232   g_option_context_set_summary (context, _("Get the value of KEY"));
233   g_option_context_set_description (context,
234     _("Arguments:\n"
235       "  SCHEMA      The id of the schema\n"
236       "  KEY         The name of the key\n"));
237   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
238
239   settings = NULL;
240   path = NULL;
241   schema = NULL;
242   key = NULL;
243
244   error = NULL;
245   if (!g_option_context_parse (context, argc, argv, NULL))
246     {
247       if (!request_completion)
248         {
249           gchar *s;
250           s = g_option_context_get_help (context, FALSE, NULL);
251           g_printerr ("%s", s);
252           g_free (s);
253
254           goto out;
255         }
256     }
257
258   if (*argc > 1)
259     schema = (*argv)[1];
260   if (*argc > 2)
261     key = (*argv)[2];
262
263   if (request_completion && completion_cur[0] == '-')
264     {
265       list_options (context, completion_cur);
266       ret = 0;
267       goto out;
268     }
269
270   if (request_completion && !schema_exists (schema))
271     {
272       list_schemas (schema);
273       ret = 0;
274       goto out;
275     }
276
277   if (path)
278     settings = g_settings_new_with_path (schema, path);
279   else
280     settings = g_settings_new (schema);
281
282   if (request_completion && !key_exists (settings, key))
283     {
284       list_keys (settings, key);
285       ret = 0;
286       goto out;
287     }
288
289   if (!request_completion)
290     {
291       v = g_settings_get_value (settings, key);
292       g_print ("%s\n", g_variant_print (v, FALSE));
293       g_variant_unref (v);
294       ret = 0;
295     }
296
297  out:
298   if (settings)
299     g_object_unref (settings);
300
301   g_option_context_free (context);
302
303   return ret;
304 }
305
306 static gint
307 handle_set (gint      *argc,
308             gchar    **argv[],
309             gboolean   request_completion,
310             gchar     *completion_cur,
311             gchar     *completion_prev)
312 {
313   gchar *schema;
314   gchar *path;
315   gchar *key;
316   gchar *value;
317   GSettings *settings;
318   GVariant *v, *default_v;
319   const GVariantType *type;
320   GOptionContext *context;
321   GOptionEntry entries[] = {
322     { "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
323     { NULL }
324   };
325   GError *error;
326   gint ret = 1;
327
328   modify_argv0_for_command (argc, argv, "set");
329
330   context = g_option_context_new (_("SCHEMA KEY VALUE"));
331   g_option_context_set_help_enabled (context, FALSE);
332   g_option_context_set_summary (context, _("Set the value of KEY"));
333   g_option_context_set_description (context,
334     _("Arguments:\n"
335       "  SCHEMA      The id of the schema\n"
336       "  KEY         The name of the key\n"
337       "  VALUE       The value to set key to, as a serialized GVariant\n"));
338   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
339
340   settings = NULL;
341   path = NULL;
342   schema = NULL;
343   key = NULL;
344
345   error = NULL;
346   if (!g_option_context_parse (context, argc, argv, NULL))
347     {
348       if (!request_completion)
349         {
350           gchar *s;
351           s = g_option_context_get_help (context, FALSE, NULL);
352           g_printerr ("%s", s);
353           g_free (s);
354           goto out;
355         }
356     }
357
358   if (*argc > 1)
359     schema = (*argv)[1];
360   if (*argc > 2)
361     key = (*argv)[2];
362   if (*argc > 3)
363     value = (*argv)[3];
364
365   if (request_completion && completion_cur[0] == '-')
366     {
367       list_options (context, completion_cur);
368       ret = 0;
369       goto out;
370     }
371
372   if (request_completion && !schema_exists (schema))
373     {
374       list_schemas (schema);
375       ret = 0;
376       goto out;
377     }
378
379   if (path)
380     settings = g_settings_new_with_path (schema, path);
381   else
382     settings = g_settings_new (schema);
383
384   if (request_completion && !key_exists (settings, key))
385     {
386       list_keys (settings, key);
387       ret = 0;
388       goto out;
389     }
390
391   if (!request_completion)
392     {
393       default_v = g_settings_get_value (settings, key);
394       type = g_variant_get_type (default_v);
395
396       error = NULL;
397       v = g_variant_parse (type, value, NULL, NULL, &error);
398       g_variant_unref (default_v);
399       if (v == NULL)
400         {
401           g_printerr ("%s\n", error->message);
402           goto out;
403         }
404
405       if (!g_settings_set_value (settings, key, v))
406         {
407           g_printerr (_("Key %s is not writable\n"), key);
408           goto out;
409         }
410
411       g_settings_sync ();
412       ret = 0;
413     }
414
415  out:
416   if (settings)
417     g_object_unref (settings);
418
419   g_option_context_free (context);
420
421   return ret;
422 }
423
424
425 static gint
426 handle_reset (gint      *argc,
427               gchar    **argv[],
428               gboolean   request_completion,
429               gchar     *completion_cur,
430               gchar     *completion_prev)
431 {
432   gchar *schema;
433   gchar *path;
434   gchar *key;
435   GSettings *settings;
436   GOptionContext *context;
437   GOptionEntry entries[] = {
438     { "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
439     { NULL }
440   };
441   GError *error;
442   gint ret = 1;
443
444   modify_argv0_for_command (argc, argv, "reset");
445
446   context = g_option_context_new (_("SCHEMA KEY VALUE"));
447   g_option_context_set_help_enabled (context, FALSE);
448   g_option_context_set_summary (context, _("Sets KEY to its default value"));
449   g_option_context_set_description (context,
450     _("Arguments:\n"
451       "  SCHEMA      The id of the schema\n"
452       "  KEY         The name of the key\n"));
453   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
454
455   settings = NULL;
456   path = NULL;
457   schema = NULL;
458   key = NULL;
459
460   error = NULL;
461   if (!g_option_context_parse (context, argc, argv, NULL))
462     {
463       if (!request_completion)
464         {
465           gchar *s;
466           s = g_option_context_get_help (context, FALSE, NULL);
467           g_printerr ("%s", s);
468           g_free (s);
469           goto out;
470         }
471     }
472
473   if (*argc > 1)
474     schema = (*argv)[1];
475   if (*argc > 2)
476     key = (*argv)[2];
477
478   if (request_completion && completion_cur[0] == '-')
479     {
480       list_options (context, completion_cur);
481       ret = 0;
482       goto out;
483     }
484
485   if (request_completion && !schema_exists (schema))
486     {
487       list_schemas (schema);
488       ret = 0;
489       goto out;
490     }
491
492   if (path)
493     settings = g_settings_new_with_path (schema, path);
494   else
495     settings = g_settings_new (schema);
496
497   if (request_completion && !key_exists (settings, key))
498     {
499       list_keys (settings, key);
500       ret = 0;
501       goto out;
502     }
503
504   if (!request_completion)
505     {
506       g_settings_reset (settings, key);
507       g_settings_sync ();
508       ret = 0;
509     }
510
511  out:
512   if (settings)
513     g_object_unref (settings);
514
515   g_option_context_free (context);
516
517   return ret;
518 }
519
520 static gint
521 handle_writable (gint   *argc,
522                  gchar **argv[],
523                  gboolean   request_completion,
524                  gchar     *completion_cur,
525                  gchar     *completion_prev)
526 {
527   gchar *schema;
528   gchar *path;
529   gchar *key;
530   GSettings *settings;
531   GOptionContext *context;
532   GOptionEntry entries[] = {
533     { "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
534     { NULL }
535   };
536   GError *error;
537   gint ret = 1;
538
539   modify_argv0_for_command (argc, argv, "writable");
540
541   context = g_option_context_new (_("SCHEMA KEY"));
542   g_option_context_set_help_enabled (context, FALSE);
543   g_option_context_set_summary (context, _("Find out whether KEY is writable"));
544   g_option_context_set_description (context,
545     _("Arguments:\n"
546       "  SCHEMA      The id of the schema\n"
547       "  KEY         The name of the key\n"));
548   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
549
550   settings = NULL;
551   path = NULL;
552   schema = NULL;
553   key = NULL;
554
555   error = NULL;
556   if (!g_option_context_parse (context, argc, argv, NULL))
557     {
558       if (!request_completion)
559         {
560           gchar *s;
561           s = g_option_context_get_help (context, FALSE, NULL);
562           g_printerr ("%s", s);
563           g_free (s);
564           goto out;
565         }
566     }
567
568   if (*argc > 1)
569     schema = (*argv)[1];
570   if (*argc > 2)
571     key = (*argv)[2];
572
573   if (request_completion && completion_cur[0] == '-')
574     {
575       list_options (context, completion_cur);
576       ret = 0;
577       goto out;
578     }
579
580   if (request_completion && !schema_exists (schema))
581     {
582       list_schemas (schema);
583       ret = 0;
584       goto out;
585     }
586
587   if (path)
588     settings = g_settings_new_with_path (schema, path);
589   else
590     settings = g_settings_new (schema);
591
592   if (request_completion && !key_exists (settings, key))
593     {
594       list_keys (settings, key);
595       ret = 0;
596       goto out;
597     }
598
599   if (!request_completion)
600     {
601       if (g_settings_is_writable (settings, key))
602         g_print ("true\n");
603       else
604         g_print ("false\n");
605       ret = 0;
606     }
607
608  out:
609   if (settings)
610     g_object_unref (settings);
611
612   g_option_context_free (context);
613
614   return ret;
615 }
616
617 static void
618 key_changed (GSettings   *settings,
619              const gchar *key)
620 {
621   GVariant *v;
622   gchar *value;
623
624   v = g_settings_get_value (settings, key);
625   value = g_variant_print (v, FALSE);
626   g_print ("%s\n", value);
627   g_free (value);
628   g_variant_unref (v);
629 }
630
631 static gint
632 handle_monitor (gint      *argc,
633                 gchar    **argv[],
634                 gboolean   request_completion,
635                 gchar     *completion_cur,
636                 gchar     *completion_prev)
637 {
638   gchar *schema;
639   gchar *path;
640   gchar *key;
641   GSettings *settings;
642   gchar *detailed_signal;
643   GMainLoop *loop;
644   GOptionContext *context;
645   GOptionEntry entries[] = {
646     { "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
647     { NULL }
648   };
649   GError *error;
650   gint ret = 1;
651
652   modify_argv0_for_command (argc, argv, "monitor");
653
654   context = g_option_context_new (_("SCHEMA KEY"));
655   g_option_context_set_help_enabled (context, FALSE);
656   g_option_context_set_summary (context,
657     _("Monitor KEY for changes and print the changed values.\n"
658       "Monitoring will continue until the process is terminated."));
659
660   g_option_context_set_description (context,
661     _("Arguments:\n"
662       "  SCHEMA      The id of the schema\n"
663       "  KEY         The name of the key\n"));
664   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
665
666   settings = NULL;
667   path = NULL;
668   schema = NULL;
669   key = NULL;
670
671   error = NULL;
672   if (!g_option_context_parse (context, argc, argv, NULL))
673     {
674       if (!request_completion)
675         {
676           gchar *s;
677           s = g_option_context_get_help (context, FALSE, NULL);
678           g_printerr ("%s", s);
679           g_free (s);
680           goto out;
681         }
682     }
683
684   if (*argc > 1)
685     schema = (*argv)[1];
686   if (*argc > 2)
687     key = (*argv)[2];
688
689   if (request_completion && completion_cur[0] == '-')
690     {
691       list_options (context, completion_cur);
692       ret = 0;
693       goto out;
694     }
695
696   if (request_completion && !schema_exists (schema))
697     {
698       list_schemas (schema);
699       ret = 0;
700       goto out;
701     }
702
703   if (path)
704     settings = g_settings_new_with_path (schema, path);
705   else
706     settings = g_settings_new (schema);
707
708   if (request_completion && !key_exists (settings, key))
709     {
710       list_keys (settings, key);
711       ret = 0;
712       goto out;
713     }
714
715   if (!request_completion)
716     {
717       detailed_signal = g_strdup_printf ("changed::%s", key);
718       g_signal_connect (settings, detailed_signal,
719                         G_CALLBACK (key_changed), NULL);
720
721       loop = g_main_loop_new (NULL, FALSE);
722       g_main_loop_run (loop);
723       g_main_loop_unref (loop);
724       ret = 0;
725     }
726
727  out:
728   if (settings)
729     g_object_unref (settings);
730
731   g_option_context_free (context);
732
733   return ret;
734 }
735
736 int
737 main (int argc, char *argv[])
738 {
739   gboolean ret;
740   gchar *command;
741   gboolean request_completion;
742   gchar *completion_cur;
743   gchar *completion_prev;
744
745   setlocale (LC_ALL, "");
746
747   g_type_init ();
748
749   ret = 1;
750   completion_cur = NULL;
751   completion_prev = NULL;
752   request_completion = FALSE;
753
754   if (argc < 2)
755     {
756       ret = usage (&argc, &argv, FALSE);
757       goto out;
758     }
759
760  again:
761   command = argv[1];
762
763   if (g_strcmp0 (command, "help") == 0)
764     {
765       if (!request_completion)
766         ret = usage (&argc, &argv, TRUE);
767     }
768   else if (g_strcmp0 (command, "get") == 0)
769     ret = handle_get (&argc, &argv, request_completion, completion_cur, completion_prev);
770   else if (g_strcmp0 (command, "set") == 0)
771     ret = handle_set (&argc, &argv, request_completion, completion_cur, completion_prev);
772   else if (g_strcmp0 (command, "reset") == 0)
773     ret = handle_reset (&argc, &argv, request_completion, completion_cur, completion_prev);
774   else if (g_strcmp0 (command, "monitor") == 0)
775     ret = handle_monitor (&argc, &argv, request_completion, completion_cur, completion_prev);
776   else if (g_strcmp0 (command, "writable") == 0)
777     ret = handle_writable (&argc, &argv, request_completion, completion_cur, completion_prev);
778   else if (g_strcmp0 (command, "complete") == 0 && argc == 4 && !request_completion)
779     {
780       gchar *completion_line;
781       gint completion_point;
782       gchar *endp;
783       gchar **completion_argv;
784       gint completion_argc;
785       gint cur_begin;
786
787       request_completion = TRUE;
788
789       completion_line = argv[2];
790       completion_point = strtol (argv[3], &endp, 10);
791       if (endp == argv[3] || *endp != '\0')
792         goto out;
793
794       if (!g_shell_parse_argv (completion_line,
795                                &completion_argc,
796                                &completion_argv,
797                                NULL))
798         {
799           /* can't parse partical cmdline, don't attempt completion */
800           goto out;
801         }
802
803       completion_prev = NULL;
804       completion_cur = pick_word_at (completion_line, completion_point, &cur_begin);
805       if (cur_begin > 0)
806         {
807           gint prev_end;
808           for (prev_end = cur_begin - 1; prev_end >= 0; prev_end--)
809             {
810               if (!g_ascii_isspace (completion_line[prev_end]))
811                 {
812                   completion_prev = pick_word_at (completion_line, prev_end, NULL);
813                   break;
814                 }
815             }
816         }
817
818       argc = completion_argc;
819       argv = completion_argv;
820
821       ret = 0;
822       goto again;
823     }
824   else
825     {
826       if (request_completion)
827         {
828           g_print ("help \nget \nmonitor \nwritable \nset \nreset \n");
829           ret = 0;
830         }
831       else
832         {
833           g_printerr (_("Unknown command '%s'\n"), argv[1]);
834           ret = usage (&argc, &argv, FALSE);
835         }
836     }
837
838  out:
839   g_free (completion_cur);
840   g_free (completion_prev);
841
842   return ret;
843 }