1// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <assert.h>
6#include <glib.h>
7#include <ibus.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string>
11
12namespace {
13
14const gchar kDummySection[] = "aaa/bbb";
15const gchar kDummyConfigName[] = "ccc";
16
17const gboolean kDummyValueBoolean = TRUE;
18const gint kDummyValueInt = 12345;
19const gdouble kDummyValueDouble = 2345.5432;
20const gchar kDummyValueString[] = "dummy value";
21
22const size_t kArraySize = 3;
23const gboolean kDummyValueBooleanArray[kArraySize] = { FALSE, TRUE, FALSE };
24const gint kDummyValueIntArray[kArraySize] = { 123, 234, 345 };
25const gdouble kDummyValueDoubleArray[kArraySize] = { 111.22, 333.44, 555.66 };
26const gchar* kDummyValueStringArray[kArraySize] = {
27  "DUMMY_VALUE 1", "DUMMY_VALUE 2", "DUMMY_VALUE 3",
28};
29
30const char kGeneralSectionName[] = "general";
31const char kPreloadEnginesConfigName[] = "preload_engines";
32
33// Converts |list_type_string| into its element type (e.g. "int_list" to "int").
34std::string GetElementType(const std::string& list_type_string) {
35  const std::string suffix = "_list";
36  if (list_type_string.length() > suffix.length()) {
37    return list_type_string.substr(
38        0, list_type_string.length() - suffix.length());
39  }
40  return list_type_string;
41}
42
43// Converts |type_string| into GVariantClass.
44GVariantClass GetGVariantClassFromStringOrDie(const std::string& type_string) {
45  if (type_string == "boolean") {
46    return G_VARIANT_CLASS_BOOLEAN;
47  } else if (type_string == "int") {
48    return G_VARIANT_CLASS_INT32;
49  } else if (type_string == "double") {
50    return G_VARIANT_CLASS_DOUBLE;
51  } else if (type_string == "string") {
52    return G_VARIANT_CLASS_STRING;
53  } else if (GetElementType(type_string) != type_string) {
54    return G_VARIANT_CLASS_ARRAY;
55  }
56  printf("FAIL (unknown type: %s)\n", type_string.c_str());
57  abort();
58}
59
60// Unsets a dummy value from ibus config service.
61void UnsetConfigAndPrintResult(IBusConfig* ibus_config) {
62  if (ibus_config_unset(ibus_config, kDummySection, kDummyConfigName)) {
63    printf("OK\n");
64  } else {
65    printf("FAIL\n");
66  }
67}
68
69// Sets a dummy value to ibus config service. You can specify a type of the
70// dummy value by |type_string|. "boolean", "int", "double", or "string" are
71// allowed.
72void SetConfigAndPrintResult(
73    IBusConfig* ibus_config, const std::string& type_string) {
74  GVariant* variant = NULL;
75  GVariantClass klass = GetGVariantClassFromStringOrDie(type_string);
76
77  switch (klass) {
78    case G_VARIANT_CLASS_BOOLEAN:
79      variant = g_variant_new_boolean(kDummyValueBoolean);
80      break;
81    case G_VARIANT_CLASS_INT32:
82      variant = g_variant_new_int32(kDummyValueInt);
83      break;
84    case G_VARIANT_CLASS_DOUBLE:
85      variant = g_variant_new_double(kDummyValueDouble);
86      break;
87    case G_VARIANT_CLASS_STRING:
88      variant = g_variant_new_string(kDummyValueString);
89      break;
90    case G_VARIANT_CLASS_ARRAY: {
91      const GVariantClass element_klass
92          = GetGVariantClassFromStringOrDie(GetElementType(type_string));
93      g_assert(element_klass != G_VARIANT_CLASS_ARRAY);
94
95      GVariantBuilder variant_builder;
96      for (size_t i = 0; i < kArraySize; ++i) {
97        switch (element_klass) {
98          case G_VARIANT_CLASS_BOOLEAN:
99            if (i == 0) {
100              g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("ab"));
101            }
102            g_variant_builder_add(
103                &variant_builder, "b", kDummyValueBooleanArray[i]);
104            break;
105          case G_VARIANT_CLASS_INT32:
106            if (i == 0) {
107              g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("ai"));
108            }
109            g_variant_builder_add(
110                &variant_builder, "i", kDummyValueIntArray[i]);
111            break;
112          case G_VARIANT_CLASS_DOUBLE:
113            if (i == 0) {
114              g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("ad"));
115            }
116            g_variant_builder_add(
117                &variant_builder, "d", kDummyValueDoubleArray[i]);
118            break;
119          case G_VARIANT_CLASS_STRING:
120            if (i == 0) {
121              g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("as"));
122            }
123            g_variant_builder_add(
124                &variant_builder, "s", kDummyValueStringArray[i]);
125            break;
126          default:
127            printf("FAIL\n");
128            return;
129        }
130      }
131      variant = g_variant_builder_end(&variant_builder);
132      break;
133    }
134    default:
135      printf("FAIL\n");
136      return;
137  }
138  if (!variant) {
139    printf("FAIL\n");
140    return;
141  }
142  if (ibus_config_set_value(
143          ibus_config, kDummySection, kDummyConfigName, variant)) {
144    printf("OK\n");
145    return;
146  }
147  printf("FAIL\n");
148}
149
150// Gets a dummy value from ibus config service. This function checks if the
151// dummy value is |type_string| type.
152void GetConfigAndPrintResult(
153    IBusConfig* ibus_config, const std::string& type_string) {
154  GVariant* variant = ibus_config_get_value(
155      ibus_config, kDummySection, kDummyConfigName);
156  if (!variant) {
157    printf("FAIL (not found)\n");
158    return;
159  }
160  switch(g_variant_classify(variant)) {
161    case G_VARIANT_CLASS_BOOLEAN: {
162      if (g_variant_get_boolean(variant) != kDummyValueBoolean) {
163        printf("FAIL (value mismatch)\n");
164        return;
165      }
166      break;
167    }
168    case G_VARIANT_CLASS_INT32: {
169      if (g_variant_get_int32(variant) != kDummyValueInt) {
170        printf("FAIL (value mismatch)\n");
171        return;
172      }
173      break;
174    }
175    case G_VARIANT_CLASS_DOUBLE: {
176      if (g_variant_get_double(variant) != kDummyValueDouble) {
177        printf("FAIL (value mismatch)\n");
178        return;
179      }
180      break;
181    }
182    case G_VARIANT_CLASS_STRING: {
183      const char* value = g_variant_get_string(variant, NULL);
184      if (value == NULL ||
185          value != std::string(kDummyValueString)) {
186        printf("FAIL (value mismatch)\n");
187        return;
188      }
189      break;
190    }
191    case G_VARIANT_CLASS_ARRAY: {
192      const GVariantType* variant_element_type
193          = g_variant_type_element(g_variant_get_type(variant));
194      GVariantIter iter;
195      g_variant_iter_init(&iter, variant);
196
197      size_t i;
198      GVariant* element = g_variant_iter_next_value(&iter);
199      for (i = 0; element; ++i) {
200        bool match = false;
201        if (g_variant_type_equal(
202                variant_element_type, G_VARIANT_TYPE_BOOLEAN)) {
203          const gboolean value = g_variant_get_boolean(element);
204          match = (value == kDummyValueBooleanArray[i]);
205        } else if (g_variant_type_equal(
206            variant_element_type, G_VARIANT_TYPE_INT32)) {
207          const gint32 value = g_variant_get_int32(element);
208          match = (value == kDummyValueIntArray[i]);
209        } else if (g_variant_type_equal(
210            variant_element_type, G_VARIANT_TYPE_DOUBLE)) {
211          const gdouble value = g_variant_get_double(element);
212          match = (value == kDummyValueDoubleArray[i]);
213        } else if (g_variant_type_equal(
214            variant_element_type, G_VARIANT_TYPE_STRING)) {
215          const char* value = g_variant_get_string(element, NULL);
216          match = (value && (value == std::string(kDummyValueStringArray[i])));
217        } else {
218          printf("FAIL (list type mismatch)\n");
219          return;
220        }
221        if (!match) {
222          printf("FAIL (value mismatch)\n");
223          return;
224        }
225        g_variant_unref(element);
226        element = g_variant_iter_next_value(&iter);
227      }
228      if (i != kArraySize) {
229        printf("FAIL (invalid array)\n");
230        return;
231      }
232      break;
233    }
234    default:
235      printf("FAIL (unknown type)\n");
236      return;
237  }
238  printf("OK\n");
239}
240
241// Prints out the array. It is assumed that the array contains STRING values.
242// On success, returns true
243// On failure, prints out "FAIL (error message)" and returns false
244bool PrintArray(GVariant* variant) {
245  if (g_variant_classify(variant) != G_VARIANT_CLASS_ARRAY) {
246    printf("FAIL (Not an array)\n");
247    return false;
248  }
249  const GVariantType* variant_element_type
250      = g_variant_type_element(g_variant_get_type(variant));
251  if (!g_variant_type_equal(variant_element_type, G_VARIANT_TYPE_STRING)) {
252    printf("FAIL (Array element type is not STRING)\n");
253    return false;
254  }
255  GVariantIter iter;
256  g_variant_iter_init(&iter, variant);
257  GVariant* element = g_variant_iter_next_value(&iter);
258  while(element) {
259    const char* value = g_variant_get_string(element, NULL);
260    if (!value) {
261      printf("FAIL (Array element type is NULL)\n");
262      return false;
263    }
264    printf("%s\n", value);
265    element = g_variant_iter_next_value(&iter);
266  }
267  return true;
268}
269
270// Print out the list of unused config variables from ibus.
271// On failure, prints out "FAIL (error message)" instead.
272void PrintUnused(IBusConfig* ibus_config) {
273  GVariant* unread = NULL;
274  GVariant* unwritten = NULL;
275  if (!ibus_config_get_unused(ibus_config, &unread, &unwritten)) {
276    printf("FAIL (get_unused failed)\n");
277    return;
278  }
279
280  if (g_variant_classify(unread) != G_VARIANT_CLASS_ARRAY) {
281    printf("FAIL (unread is not an array)\n");
282    g_variant_unref(unread);
283    g_variant_unref(unwritten);
284    return;
285  }
286
287  if (g_variant_classify(unwritten) != G_VARIANT_CLASS_ARRAY) {
288    printf("FAIL (unwritten is not an array)\n");
289    g_variant_unref(unread);
290    g_variant_unref(unwritten);
291    return;
292  }
293
294  printf("Unread:\n");
295  if (!PrintArray(unread)) {
296    g_variant_unref(unread);
297    g_variant_unref(unwritten);
298    return;
299  }
300
301  printf("Unwritten:\n");
302  if (!PrintArray(unwritten)) {
303    g_variant_unref(unread);
304    g_variant_unref(unwritten);
305    return;
306  }
307
308  g_variant_unref(unread);
309  g_variant_unref(unwritten);
310}
311
312// Set the preload engines to those named in the array |engines| of size
313// |num_engines| and prints the result.
314//
315// Note that this only fails if it can't set the config value; it does not check
316// that the names of the engines are valid.
317void PreloadEnginesAndPrintResult(IBusConfig* ibus_config, int num_engines,
318                                  char** engines) {
319  GVariant* variant = NULL;
320  GVariantBuilder variant_builder;
321  g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("as"));
322  for (int i = 0; i < num_engines; ++i) {
323    g_variant_builder_add(&variant_builder, "s", engines[i]);
324  }
325  variant = g_variant_builder_end(&variant_builder);
326
327  if (ibus_config_set_value(ibus_config, kGeneralSectionName,
328                            kPreloadEnginesConfigName, variant)) {
329    printf("OK\n");
330  } else {
331    printf("FAIL\n");
332  }
333  g_variant_unref(variant);
334}
335
336// Sets |engine_name| as the active IME engine.
337void ActivateEngineAndPrintResult(IBusBus* ibus, const char* engine_name) {
338  if (!ibus_bus_set_global_engine(ibus, engine_name)) {
339    printf("FAIL (could not start engine)\n");
340  } else {
341    printf("OK\n");
342  }
343}
344
345// Prints the name of the active IME engine.
346void PrintActiveEngine(IBusBus* ibus) {
347  IBusEngineDesc* engine_desc = ibus_bus_get_global_engine(ibus);
348  if (engine_desc) {
349    printf("%s\n", ibus_engine_desc_get_name(engine_desc));
350    g_object_unref(engine_desc);
351  } else {
352    printf("FAIL (Could not get active engine)\n");
353  }
354}
355
356// Prints the names of the given engines. Takes the ownership of |engines|.
357void PrintEngineNames(GList* engines) {
358  for (GList* cursor = engines; cursor; cursor = g_list_next(cursor)) {
359    IBusEngineDesc* engine_desc = IBUS_ENGINE_DESC(cursor->data);
360    assert(engine_desc);
361    printf("%s\n", ibus_engine_desc_get_name(engine_desc));
362    g_object_unref(IBUS_ENGINE_DESC(cursor->data));
363  }
364  g_list_free(engines);
365}
366
367void PrintUsage(const char* argv0) {
368  printf("Usage: %s COMMAND\n", argv0);
369  printf("check_reachable      Check if ibus-daemon is reachable\n");
370  printf("list_engines         List engine names (all engines)\n");
371  printf("list_active_engines  List active engine names\n");
372  // TODO(yusukes): Add 2 parameters, config_key and config_value, to
373  // set_config and get_config commands.
374  printf("set_config (boolean|int|double|string|\n"
375         "            boolean_list|int_list|double_list|string_list)\n"
376         "                     Set a dummy value to ibus config service\n");
377  printf("get_config (boolean|int|double|string\n"
378         "            boolean_list|int_list|double_list|string_list)\n"
379         "                     Get a dummy value from ibus config service\n");
380  // TODO(yusukes): Add config_key parameter to unset_config.
381  printf("unset_config         Unset a dummy value from ibus config service\n");
382  printf("get_unused           List all keys that never were used.\n");
383  printf("preload_engines      Preload the listed engines.\n");
384  printf("activate_engine      Activate the specified engine.\n");
385  printf("get_active_engine    Print the name of the current active engine.\n");
386}
387
388}  // namespace
389
390int main(int argc, char **argv) {
391  if (argc == 1) {
392    PrintUsage(argv[0]);
393    return 1;
394  }
395
396  ibus_init();
397  bool connected = false;
398  IBusBus* ibus = ibus_bus_new();
399  if (ibus) {
400    connected = ibus_bus_is_connected(ibus);
401  }
402
403  const std::string command = argv[1];
404  if (command == "check_reachable") {
405    printf("%s\n", connected ? "YES" : "NO");
406    return 0;
407  } else if (!connected) {
408    printf("FAIL (Not connected)\n");
409    return 0;
410  }
411
412  // Other commands need the bus to be connected.
413  assert(ibus);
414  assert(connected);
415  GDBusConnection* ibus_connection = ibus_bus_get_connection(ibus);
416  assert(ibus_connection);
417  IBusConfig* ibus_config = ibus_config_new(ibus_connection, NULL, NULL);
418  assert(ibus_config);
419
420  if (command == "list_engines") {
421    PrintEngineNames(ibus_bus_list_engines(ibus));
422  } else if (command == "list_active_engines") {
423    PrintEngineNames(ibus_bus_list_active_engines(ibus));
424  } else if (command == "set_config") {
425    if (argc != 3) {
426      PrintUsage(argv[0]);
427      return 1;
428    }
429    SetConfigAndPrintResult(ibus_config, argv[2]);
430  } else if (command == "get_config") {
431    if (argc != 3) {
432      PrintUsage(argv[0]);
433      return 1;
434    }
435    GetConfigAndPrintResult(ibus_config, argv[2]);
436  } else if (command == "unset_config") {
437    UnsetConfigAndPrintResult(ibus_config);
438  } else if (command == "get_unused") {
439    PrintUnused(ibus_config);
440  } else if (command == "preload_engines") {
441    if (argc < 3) {
442      PrintUsage(argv[0]);
443      return 1;
444    }
445    PreloadEnginesAndPrintResult(ibus_config, argc-2, &(argv[2]));
446  } else if (command == "activate_engine") {
447    if (argc != 3) {
448      PrintUsage(argv[0]);
449      return 1;
450    }
451    ActivateEngineAndPrintResult(ibus, argv[2]);
452  } else if (command == "get_active_engine") {
453    PrintActiveEngine(ibus);
454  } else {
455    PrintUsage(argv[0]);
456    return 1;
457  }
458
459  return 0;
460}
461