g_settings_list_keys() -> _list_items()
[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_items (settings);
166   for (i = 0; keys[i]; i++)
167     if (!g_str_has_suffix (keys[i], "/") &&
168         g_strcmp0 (keys[i], name) == 0)
169       {
170         ret = TRUE;
171         break;
172       }
173   g_free (keys);
174
175   return ret;
176 }
177
178 static void
179 list_keys (GSettings   *settings,
180            const gchar *prefix)
181 {
182   const gchar **keys;
183   gint i;
184
185   keys = g_settings_list_items (settings);
186   for (i = 0; keys[i]; i++)
187     {
188       if (!g_str_has_suffix (keys[i], "/") &&
189           (prefix == NULL || g_str_has_prefix (keys[i], prefix)))
190         g_print ("%s \n", keys[i]);
191     }
192   g_free (keys);
193 }
194
195 static void
196 list_options (GOptionContext *context,
197               const gchar    *prefix)
198 {
199   /* FIXME extract options from context */
200   const gchar *options[] = { "--help", "--path", NULL };
201   gint i;
202   for (i = 0; options[i]; i++)
203     if (g_str_has_prefix (options[i], prefix))
204       g_print ("%s \n", options[i]);
205 }
206
207 static gint
208 handle_get (gint      *argc,
209             gchar    **argv[],
210             gboolean   request_completion,
211             gchar     *completion_cur,
212             gchar     *completion_prev)
213 {
214   gchar *schema;
215   gchar *path;
216   gchar *key;
217   GSettings *settings;
218   GVariant *v;
219   GOptionContext *context;
220   GOptionEntry entries[] = {
221     { "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
222     { NULL }
223   };
224   GError *error;
225   gint ret = 1;
226
227   modify_argv0_for_command (argc, argv, "get");
228
229   context = g_option_context_new (_("SCHEMA KEY"));
230   g_option_context_set_help_enabled (context, FALSE);
231   g_option_context_set_summary (context, _("Get the value of KEY"));
232   g_option_context_set_description (context,
233     _("Arguments:\n"
234       "  SCHEMA      The id of the schema\n"
235       "  KEY         The name of the key\n"));
236   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
237
238   settings = NULL;
239   path = NULL;
240   schema = NULL;
241   key = NULL;
242
243   error = NULL;
244   if (!g_option_context_parse (context, argc, argv, NULL))
245     {
246       if (!request_completion)
247         {
248           gchar *s;
249           s = g_option_context_get_help (context, FALSE, NULL);
250           g_printerr ("%s", s);
251           g_free (s);
252
253           goto out;
254         }
255     }
256
257   if (*argc > 1)
258     schema = (*argv)[1];
259   if (*argc > 2)
260     key = (*argv)[2];
261
262   if (request_completion && completion_cur[0] == '-')
263     {
264       list_options (context, completion_cur);
265       ret = 0;
266       goto out;
267     }
268
269   if (request_completion && !schema_exists (schema))
270     {
271       list_schemas (schema);
272       ret = 0;
273       goto out;
274     }
275
276   if (path)
277     settings = g_settings_new_with_path (schema, path);
278   else
279     settings = g_settings_new (schema);
280
281   if (request_completion && !key_exists (settings, key))
282     {
283       list_keys (settings, key);
284       ret = 0;
285       goto out;
286     }
287
288   if (!request_completion)
289     {
290       v = g_settings_get_value (settings, key);
291       g_print ("%s\n", g_variant_print (v, FALSE));
292       g_variant_unref (v);
293       ret = 0;
294     }
295
296  out:
297   if (settings)
298     g_object_unref (settings);
299
300   g_option_context_free (context);
301
302   return ret;
303 }
304
305 static gint
306 handle_set (gint      *argc,
307             gchar    **argv[],
308             gboolean   request_completion,
309             gchar     *completion_cur,
310             gchar     *completion_prev)
311 {
312   gchar *schema;
313   gchar *path;
314   gchar *key;
315   gchar *value;
316   GSettings *settings;
317   GVariant *v, *default_v;
318   const GVariantType *type;
319   GOptionContext *context;
320   GOptionEntry entries[] = {
321     { "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
322     { NULL }
323   };
324   GError *error;
325   gint ret = 1;
326
327   modify_argv0_for_command (argc, argv, "set");
328
329   context = g_option_context_new (_("SCHEMA KEY VALUE"));
330   g_option_context_set_help_enabled (context, FALSE);
331   g_option_context_set_summary (context, _("Set the value of KEY"));
332   g_option_context_set_description (context,
333     _("Arguments:\n"
334       "  SCHEMA      The id of the schema\n"
335       "  KEY         The name of the key\n"
336       "  VALUE       The value to set key to, as a serialized GVariant\n"));
337   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
338
339   settings = NULL;
340   path = NULL;
341   schema = NULL;
342   key = NULL;
343
344   error = NULL;
345   if (!g_option_context_parse (context, argc, argv, NULL))
346     {
347       if (!request_completion)
348         {
349           gchar *s;
350           s = g_option_context_get_help (context, FALSE, NULL);
351           g_printerr ("%s", s);
352           g_free (s);
353           goto out;
354         }
355     }
356
357   if (*argc > 1)
358     schema = (*argv)[1];
359   if (*argc > 2)
360     key = (*argv)[2];
361   if (*argc > 3)
362     value = (*argv)[3];
363
364   if (request_completion && completion_cur[0] == '-')
365     {
366       list_options (context, completion_cur);
367       ret = 0;
368       goto out;
369     }
370
371   if (request_completion && !schema_exists (schema))
372     {
373       list_schemas (schema);
374       ret = 0;
375       goto out;
376     }
377
378   if (path)
379     settings = g_settings_new_with_path (schema, path);
380   else
381     settings = g_settings_new (schema);
382
383   if (request_completion && !key_exists (settings, key))
384     {
385       list_keys (settings, key);
386       ret = 0;
387       goto out;
388     }
389
390   if (!request_completion)
391     {
392       default_v = g_settings_get_value (settings, key);
393       type = g_variant_get_type (default_v);
394
395       error = NULL;
396       v = g_variant_parse (type, value, NULL, NULL, &error);
397       g_variant_unref (default_v);
398       if (v == NULL)
399         {
400           g_printerr ("%s\n", error->message);
401           goto out;
402         }
403
404       if (!g_settings_set_value (settings, key, v))
405         {
406           g_printerr (_("Key %s is not writable\n"), key);
407           goto out;
408         }
409
410       g_settings_sync ();
411       ret = 0;
412     }
413
414  out:
415   if (settings)
416     g_object_unref (settings);
417
418   g_option_context_free (context);
419
420   return ret;
421 }
422
423 static gint
424 handle_writable (gint   *argc,
425                  gchar **argv[],
426                  gboolean   request_completion,
427                  gchar     *completion_cur,
428                  gchar     *completion_prev)
429 {
430   gchar *schema;
431   gchar *path;
432   gchar *key;
433   GSettings *settings;
434   GOptionContext *context;
435   GOptionEntry entries[] = {
436     { "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
437     { NULL }
438   };
439   GError *error;
440   gint ret = 1;
441
442   modify_argv0_for_command (argc, argv, "writable");
443
444   context = g_option_context_new (_("SCHEMA KEY"));
445   g_option_context_set_help_enabled (context, FALSE);
446   g_option_context_set_summary (context, _("Find out whether KEY is writable"));
447   g_option_context_set_description (context,
448     _("Arguments:\n"
449       "  SCHEMA      The id of the schema\n"
450       "  KEY         The name of the key\n"));
451   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
452
453   settings = NULL;
454   path = NULL;
455   schema = NULL;
456   key = NULL;
457
458   error = NULL;
459   if (!g_option_context_parse (context, argc, argv, NULL))
460     {
461       if (!request_completion)
462         {
463           gchar *s;
464           s = g_option_context_get_help (context, FALSE, NULL);
465           g_printerr ("%s", s);
466           g_free (s);
467           goto out;
468         }
469     }
470
471   if (*argc > 1)
472     schema = (*argv)[1];
473   if (*argc > 2)
474     key = (*argv)[2];
475
476   if (request_completion && completion_cur[0] == '-')
477     {
478       list_options (context, completion_cur);
479       ret = 0;
480       goto out;
481     }
482
483   if (request_completion && !schema_exists (schema))
484     {
485       list_schemas (schema);
486       ret = 0;
487       goto out;
488     }
489
490   if (path)
491     settings = g_settings_new_with_path (schema, path);
492   else
493     settings = g_settings_new (schema);
494
495   if (request_completion && !key_exists (settings, key))
496     {
497       list_keys (settings, key);
498       ret = 0;
499       goto out;
500     }
501
502   if (!request_completion)
503     {
504       if (g_settings_is_writable (settings, key))
505         g_print ("true\n");
506       else
507         g_print ("false\n");
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 void
521 key_changed (GSettings   *settings,
522              const gchar *key)
523 {
524   GVariant *v;
525   gchar *value;
526
527   v = g_settings_get_value (settings, key);
528   value = g_variant_print (v, FALSE);
529   g_print ("%s\n", value);
530   g_free (value);
531   g_variant_unref (v);
532 }
533
534 static gint
535 handle_monitor (gint      *argc,
536                 gchar    **argv[],
537                 gboolean   request_completion,
538                 gchar     *completion_cur,
539                 gchar     *completion_prev)
540 {
541   gchar *schema;
542   gchar *path;
543   gchar *key;
544   GSettings *settings;
545   gchar *detailed_signal;
546   GMainLoop *loop;
547   GOptionContext *context;
548   GOptionEntry entries[] = {
549     { "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
550     { NULL }
551   };
552   GError *error;
553   gint ret = 1;
554
555   modify_argv0_for_command (argc, argv, "monitor");
556
557   context = g_option_context_new (_("SCHEMA KEY"));
558   g_option_context_set_help_enabled (context, FALSE);
559   g_option_context_set_summary (context,
560     _("Monitor KEY for changes and print the changed values.\n"
561       "Monitoring will continue until the process is terminated."));
562
563   g_option_context_set_description (context,
564     _("Arguments:\n"
565       "  SCHEMA      The id of the schema\n"
566       "  KEY         The name of the key\n"));
567   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
568
569   settings = NULL;
570   path = NULL;
571   schema = NULL;
572   key = NULL;
573
574   error = NULL;
575   if (!g_option_context_parse (context, argc, argv, NULL))
576     {
577       if (!request_completion)
578         {
579           gchar *s;
580           s = g_option_context_get_help (context, FALSE, NULL);
581           g_printerr ("%s", s);
582           g_free (s);
583           goto out;
584         }
585     }
586
587   if (*argc > 1)
588     schema = (*argv)[1];
589   if (*argc > 2)
590     key = (*argv)[2];
591
592   if (request_completion && completion_cur[0] == '-')
593     {
594       list_options (context, completion_cur);
595       ret = 0;
596       goto out;
597     }
598
599   if (request_completion && !schema_exists (schema))
600     {
601       list_schemas (schema);
602       ret = 0;
603       goto out;
604     }
605
606   if (path)
607     settings = g_settings_new_with_path (schema, path);
608   else
609     settings = g_settings_new (schema);
610
611   if (request_completion && !key_exists (settings, key))
612     {
613       list_keys (settings, key);
614       ret = 0;
615       goto out;
616     }
617
618   if (!request_completion)
619     {
620       detailed_signal = g_strdup_printf ("changed::%s", key);
621       g_signal_connect (settings, detailed_signal,
622                         G_CALLBACK (key_changed), NULL);
623
624       loop = g_main_loop_new (NULL, FALSE);
625       g_main_loop_run (loop);
626       g_main_loop_unref (loop);
627       ret = 0;
628     }
629
630  out:
631   if (settings)
632     g_object_unref (settings);
633
634   g_option_context_free (context);
635
636   return ret;
637 }
638 int
639 main (int argc, char *argv[])
640 {
641   gboolean ret;
642   gchar *command;
643   gboolean request_completion;
644   gchar *completion_cur;
645   gchar *completion_prev;
646
647   setlocale (LC_ALL, "");
648
649   g_type_init ();
650
651   ret = 1;
652   completion_cur = NULL;
653   completion_prev = NULL;
654   request_completion = FALSE;
655
656   if (argc < 2)
657     {
658       ret = usage (&argc, &argv, FALSE);
659       goto out;
660     }
661
662  again:
663   command = argv[1];
664
665   if (g_strcmp0 (command, "help") == 0)
666     {
667       if (!request_completion)
668         ret = usage (&argc, &argv, TRUE);
669     }
670   else if (g_strcmp0 (command, "get") == 0)
671     ret = handle_get (&argc, &argv, request_completion, completion_cur, completion_prev);
672   else if (g_strcmp0 (command, "set") == 0)
673     ret = handle_set (&argc, &argv, request_completion, completion_cur, completion_prev);
674   else if (g_strcmp0 (command, "monitor") == 0)
675     ret = handle_monitor (&argc, &argv, request_completion, completion_cur, completion_prev);
676   else if (g_strcmp0 (command, "writable") == 0)
677     ret = handle_writable (&argc, &argv, request_completion, completion_cur, completion_prev);
678   else if (g_strcmp0 (command, "complete") == 0 && argc == 4 && !request_completion)
679     {
680       gchar *completion_line;
681       gint completion_point;
682       gchar *endp;
683       gchar **completion_argv;
684       gint completion_argc;
685       gint cur_begin;
686
687       request_completion = TRUE;
688
689       completion_line = argv[2];
690       completion_point = strtol (argv[3], &endp, 10);
691       if (endp == argv[3] || *endp != '\0')
692         goto out;
693
694       if (!g_shell_parse_argv (completion_line,
695                                &completion_argc,
696                                &completion_argv,
697                                NULL))
698         {
699           /* can't parse partical cmdline, don't attempt completion */
700           goto out;
701         }
702
703       completion_prev = NULL;
704       completion_cur = pick_word_at (completion_line, completion_point, &cur_begin);
705       if (cur_begin > 0)
706         {
707           gint prev_end;
708           for (prev_end = cur_begin - 1; prev_end >= 0; prev_end--)
709             {
710               if (!g_ascii_isspace (completion_line[prev_end]))
711                 {
712                   completion_prev = pick_word_at (completion_line, prev_end, NULL);
713                   break;
714                 }
715             }
716         }
717
718       argc = completion_argc;
719       argv = completion_argv;
720
721       ret = 0;
722       goto again;
723     }
724   else
725     {
726       if (request_completion)
727         {
728           g_print ("help \nget \nmonitor \nwritable \nset \n");
729           ret = 0;
730         }
731       else
732         {
733           g_printerr (_("Unknown command '%s'\n"), argv[1]);
734           ret = usage (&argc, &argv, FALSE);
735         }
736     }
737
738  out:
739   g_free (completion_cur);
740   g_free (completion_prev);
741
742   return ret;
743 }