aboutsummaryrefslogtreecommitdiffstats
path: root/sway/old/commands.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/old/commands.c')
-rw-r--r--sway/old/commands.c686
1 files changed, 686 insertions, 0 deletions
diff --git a/sway/old/commands.c b/sway/old/commands.c
new file mode 100644
index 00000000..e1181893
--- /dev/null
+++ b/sway/old/commands.c
@@ -0,0 +1,686 @@
1#define _XOPEN_SOURCE 700
2#include <xkbcommon/xkbcommon.h>
3#include <xkbcommon/xkbcommon-names.h>
4#include <wlc/wlc.h>
5#include <wlc/wlc-render.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <errno.h>
9#include <string.h>
10#include <strings.h>
11#include <unistd.h>
12#include <ctype.h>
13#include <wordexp.h>
14#include <libgen.h>
15#include <sys/types.h>
16#include <sys/wait.h>
17#include <limits.h>
18#include <float.h>
19#include <libinput.h>
20#include "sway/layout.h"
21#include "sway/focus.h"
22#include "sway/workspace.h"
23#include "sway/commands.h"
24#include "sway/container.h"
25#include "sway/output.h"
26#include "sway/handlers.h"
27#include "sway/input_state.h"
28#include "sway/criteria.h"
29#include "sway/ipc-server.h"
30#include "sway/security.h"
31#include "sway/input.h"
32#include "sway/border.h"
33#include "stringop.h"
34#include "sway.h"
35#include "util.h"
36#include "list.h"
37#include "log.h"
38
39struct cmd_handler {
40 char *command;
41 sway_cmd *handle;
42};
43
44int sp_index = 0;
45
46swayc_t *current_container = NULL;
47
48// Returns error object, or NULL if check succeeds.
49struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) {
50 struct cmd_results *error = NULL;
51 switch (type) {
52 case EXPECTED_MORE_THAN:
53 if (argc > val) {
54 return NULL;
55 }
56 error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
57 "(expected more than %d argument%s, got %d)",
58 name, val, (char*[2]){"s", ""}[argc==1], argc);
59 break;
60 case EXPECTED_AT_LEAST:
61 if (argc >= val) {
62 return NULL;
63 }
64 error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
65 "(expected at least %d argument%s, got %d)",
66 name, val, (char*[2]){"s", ""}[argc==1], argc);
67 break;
68 case EXPECTED_LESS_THAN:
69 if (argc < val) {
70 return NULL;
71 };
72 error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
73 "(expected less than %d argument%s, got %d)",
74 name, val, (char*[2]){"s", ""}[argc==1], argc);
75 break;
76 case EXPECTED_EQUAL_TO:
77 if (argc == val) {
78 return NULL;
79 };
80 error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
81 "(expected %d arguments, got %d)", name, val, argc);
82 break;
83 }
84 return error;
85}
86
87void hide_view_in_scratchpad(swayc_t *sp_view) {
88 if (sp_view == NULL) {
89 return;
90 }
91
92 wlc_view_set_mask(sp_view->handle, 0);
93 sp_view->visible = false;
94 swayc_t *ws = sp_view->parent;
95 remove_child(sp_view);
96 if (swayc_active_workspace() != ws && ws->floating->length == 0 && ws->children->length == 0) {
97 destroy_workspace(ws);
98 } else {
99 arrange_windows(ws, -1, -1);
100 }
101 set_focused_container(container_under_pointer());
102}
103
104void input_cmd_apply(struct input_config *input) {
105 int i;
106 i = list_seq_find(config->input_configs, input_identifier_cmp, input->identifier);
107 if (i >= 0) {
108 // merge existing config
109 struct input_config *ic = config->input_configs->items[i];
110 merge_input_config(ic, input);
111 free_input_config(input);
112 input = ic;
113 } else {
114 list_add(config->input_configs, input);
115 }
116
117 current_input_config = input;
118
119 if (input->identifier) {
120 // Try to find the input device and apply configuration now. If
121 // this is during startup then there will be no container and config
122 // will be applied during normal "new input" event from wlc.
123 /* TODO WLR
124 struct libinput_device *device = NULL;
125 for (int i = 0; i < input_devices->length; ++i) {
126 device = input_devices->items[i];
127 char* dev_identifier = libinput_dev_unique_id(device);
128 if (!dev_identifier) {
129 break;
130 }
131 int match = dev_identifier && strcmp(dev_identifier, input->identifier) == 0;
132 free(dev_identifier);
133 if (match) {
134 apply_input_config(input, device);
135 break;
136 }
137 }
138 */
139 }
140}
141
142void remove_view_from_scratchpad(swayc_t *view) {
143 int i;
144 for (i = 0; i < scratchpad->length; i++) {
145 if (scratchpad->items[i] == view) {
146 if (sp_index == 0) {
147 sp_index = scratchpad->length - 1;
148 } else {
149 sp_index--;
150 }
151 list_del(scratchpad, sp_index);
152 sp_view = NULL;
153 }
154 }
155}
156
157/* Keep alphabetized */
158static struct cmd_handler handlers[] = {
159 { "assign", cmd_assign },
160 { "bar", cmd_bar },
161 { "bindcode", cmd_bindcode },
162 { "bindsym", cmd_bindsym },
163 { "border", cmd_border },
164 { "client.background", cmd_client_background },
165 { "client.focused", cmd_client_focused },
166 { "client.focused_inactive", cmd_client_focused_inactive },
167 { "client.placeholder", cmd_client_placeholder },
168 { "client.unfocused", cmd_client_unfocused },
169 { "client.urgent", cmd_client_urgent },
170 { "clipboard", cmd_clipboard },
171 { "commands", cmd_commands },
172 { "debuglog", cmd_debuglog },
173 { "default_border", cmd_default_border },
174 { "default_floating_border", cmd_default_floating_border },
175 { "default_orientation", cmd_orientation },
176 { "exec", cmd_exec },
177 { "exec_always", cmd_exec_always },
178 { "exit", cmd_exit },
179 { "floating", cmd_floating },
180 { "floating_maximum_size", cmd_floating_maximum_size },
181 { "floating_minimum_size", cmd_floating_minimum_size },
182 { "floating_modifier", cmd_floating_mod },
183 { "floating_scroll", cmd_floating_scroll },
184 { "focus", cmd_focus },
185 { "focus_follows_mouse", cmd_focus_follows_mouse },
186 { "font", cmd_font },
187 { "for_window", cmd_for_window },
188 { "force_focus_wrapping", cmd_force_focus_wrapping },
189 { "fullscreen", cmd_fullscreen },
190 { "gaps", cmd_gaps },
191 { "hide_edge_borders", cmd_hide_edge_borders },
192 { "include", cmd_include },
193 { "input", cmd_input },
194 { "ipc", cmd_ipc },
195 { "kill", cmd_kill },
196 { "layout", cmd_layout },
197 { "log_colors", cmd_log_colors },
198 { "mark", cmd_mark },
199 { "mode", cmd_mode },
200 { "mouse_warping", cmd_mouse_warping },
201 { "move", cmd_move },
202 { "new_float", cmd_new_float },
203 { "new_window", cmd_new_window },
204 { "no_focus", cmd_no_focus },
205 { "output", cmd_output },
206 { "permit", cmd_permit },
207 { "reject", cmd_reject },
208 { "reload", cmd_reload },
209 { "resize", cmd_resize },
210 { "scratchpad", cmd_scratchpad },
211 { "seamless_mouse", cmd_seamless_mouse },
212 { "set", cmd_set },
213 { "show_marks", cmd_show_marks },
214 { "smart_gaps", cmd_smart_gaps },
215 { "split", cmd_split },
216 { "splith", cmd_splith },
217 { "splitt", cmd_splitt },
218 { "splitv", cmd_splitv },
219 { "sticky", cmd_sticky },
220 { "unmark", cmd_unmark },
221 { "workspace", cmd_workspace },
222 { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth },
223 { "workspace_layout", cmd_workspace_layout },
224};
225
226static struct cmd_handler bar_handlers[] = {
227 { "activate_button", bar_cmd_activate_button },
228 { "binding_mode_indicator", bar_cmd_binding_mode_indicator },
229 { "bindsym", bar_cmd_bindsym },
230 { "colors", bar_cmd_colors },
231 { "context_button", bar_cmd_context_button },
232 { "font", bar_cmd_font },
233 { "height", bar_cmd_height },
234 { "hidden_state", bar_cmd_hidden_state },
235 { "icon_theme", bar_cmd_icon_theme },
236 { "id", bar_cmd_id },
237 { "mode", bar_cmd_mode },
238 { "modifier", bar_cmd_modifier },
239 { "output", bar_cmd_output },
240 { "pango_markup", bar_cmd_pango_markup },
241 { "position", bar_cmd_position },
242 { "secondary_button", bar_cmd_secondary_button },
243 { "separator_symbol", bar_cmd_separator_symbol },
244 { "status_command", bar_cmd_status_command },
245 { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers },
246 { "swaybar_command", bar_cmd_swaybar_command },
247 { "tray_output", bar_cmd_tray_output },
248 { "tray_padding", bar_cmd_tray_padding },
249 { "workspace_buttons", bar_cmd_workspace_buttons },
250 { "wrap_scroll", bar_cmd_wrap_scroll },
251};
252
253/**
254 * Check and add color to buffer.
255 *
256 * return error object, or NULL if color is valid.
257 */
258struct cmd_results *add_color(const char *name, char *buffer, const char *color) {
259 int len = strlen(color);
260 if (len != 7 && len != 9) {
261 return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
262 }
263
264 if (color[0] != '#') {
265 return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
266 }
267
268 int i;
269 for (i = 1; i < len; ++i) {
270 if (!isxdigit(color[i])) {
271 return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
272 }
273 }
274
275 // copy color to buffer
276 strncpy(buffer, color, len);
277 // add default alpha channel if color was defined without it
278 if (len == 7) {
279 buffer[7] = 'f';
280 buffer[8] = 'f';
281 }
282 buffer[9] = '\0';
283
284 return NULL;
285}
286
287static struct cmd_handler input_handlers[] = {
288 { "accel_profile", input_cmd_accel_profile },
289 { "click_method", input_cmd_click_method },
290 { "drag_lock", input_cmd_drag_lock },
291 { "dwt", input_cmd_dwt },
292 { "events", input_cmd_events },
293 { "left_handed", input_cmd_left_handed },
294 { "middle_emulation", input_cmd_middle_emulation },
295 { "natural_scroll", input_cmd_natural_scroll },
296 { "pointer_accel", input_cmd_pointer_accel },
297 { "scroll_method", input_cmd_scroll_method },
298 { "tap", input_cmd_tap },
299};
300
301static struct cmd_handler bar_colors_handlers[] = {
302 { "active_workspace", bar_colors_cmd_active_workspace },
303 { "background", bar_colors_cmd_background },
304 { "binding_mode", bar_colors_cmd_binding_mode },
305 { "focused_background", bar_colors_cmd_focused_background },
306 { "focused_separator", bar_colors_cmd_focused_separator },
307 { "focused_statusline", bar_colors_cmd_focused_statusline },
308 { "focused_workspace", bar_colors_cmd_focused_workspace },
309 { "inactive_workspace", bar_colors_cmd_inactive_workspace },
310 { "separator", bar_colors_cmd_separator },
311 { "statusline", bar_colors_cmd_statusline },
312 { "urgent_workspace", bar_colors_cmd_urgent_workspace },
313};
314
315static struct cmd_handler ipc_handlers[] = {
316 { "*", cmd_ipc_cmd },
317 { "bar-config", cmd_ipc_cmd },
318 { "command", cmd_ipc_cmd },
319 { "events", cmd_ipc_events },
320 { "inputs", cmd_ipc_cmd },
321 { "marks", cmd_ipc_cmd },
322 { "outputs", cmd_ipc_cmd },
323 { "tree", cmd_ipc_cmd },
324 { "workspaces", cmd_ipc_cmd },
325};
326
327static struct cmd_handler ipc_event_handlers[] = {
328 { "*", cmd_ipc_event_cmd },
329 { "binding", cmd_ipc_event_cmd },
330 { "input", cmd_ipc_event_cmd },
331 { "mode", cmd_ipc_event_cmd },
332 { "output", cmd_ipc_event_cmd },
333 { "window", cmd_ipc_event_cmd },
334 { "workspace", cmd_ipc_event_cmd },
335};
336
337static int handler_compare(const void *_a, const void *_b) {
338 const struct cmd_handler *a = _a;
339 const struct cmd_handler *b = _b;
340 return strcasecmp(a->command, b->command);
341}
342
343static struct cmd_handler *find_handler(char *line, enum cmd_status block) {
344 struct cmd_handler d = { .command=line };
345 struct cmd_handler *res = NULL;
346 sway_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_INPUT);
347 if (block == CMD_BLOCK_BAR) {
348 res = bsearch(&d, bar_handlers,
349 sizeof(bar_handlers) / sizeof(struct cmd_handler),
350 sizeof(struct cmd_handler), handler_compare);
351 } else if (block == CMD_BLOCK_BAR_COLORS){
352 res = bsearch(&d, bar_colors_handlers,
353 sizeof(bar_colors_handlers) / sizeof(struct cmd_handler),
354 sizeof(struct cmd_handler), handler_compare);
355 } else if (block == CMD_BLOCK_INPUT) {
356 res = bsearch(&d, input_handlers,
357 sizeof(input_handlers) / sizeof(struct cmd_handler),
358 sizeof(struct cmd_handler), handler_compare);
359 } else if (block == CMD_BLOCK_IPC) {
360 res = bsearch(&d, ipc_handlers,
361 sizeof(ipc_handlers) / sizeof(struct cmd_handler),
362 sizeof(struct cmd_handler), handler_compare);
363 } else if (block == CMD_BLOCK_IPC_EVENTS) {
364 res = bsearch(&d, ipc_event_handlers,
365 sizeof(ipc_event_handlers) / sizeof(struct cmd_handler),
366 sizeof(struct cmd_handler), handler_compare);
367 } else {
368 res = bsearch(&d, handlers,
369 sizeof(handlers) / sizeof(struct cmd_handler),
370 sizeof(struct cmd_handler), handler_compare);
371 }
372 return res;
373}
374
375struct cmd_results *handle_command(char *_exec, enum command_context context) {
376 // Even though this function will process multiple commands we will only
377 // return the last error, if any (for now). (Since we have access to an
378 // error string we could e.g. concatonate all errors there.)
379 struct cmd_results *results = NULL;
380 char *exec = strdup(_exec);
381 char *head = exec;
382 char *cmdlist;
383 char *cmd;
384 list_t *containers = NULL;
385
386 head = exec;
387 do {
388 // Extract criteria (valid for this command list only).
389 if (*head == '[') {
390 ++head;
391 char *criteria_string = argsep(&head, "]");
392 if (head) {
393 ++head;
394 list_t *tokens = create_list();
395 char *error;
396
397 if ((error = extract_crit_tokens(tokens, criteria_string))) {
398 results = cmd_results_new(CMD_INVALID, criteria_string,
399 "Can't parse criteria string: %s", error);
400 free(error);
401 free(tokens);
402 goto cleanup;
403 }
404 containers = container_for(tokens);
405
406 free(tokens);
407 } else {
408 if (!results) {
409 results = cmd_results_new(CMD_INVALID, criteria_string, "Unmatched [");
410 }
411 goto cleanup;
412 }
413 // Skip leading whitespace
414 head += strspn(head, whitespace);
415 }
416 // Split command list
417 cmdlist = argsep(&head, ";");
418 cmdlist += strspn(cmdlist, whitespace);
419 do {
420 // Split commands
421 cmd = argsep(&cmdlist, ",");
422 cmd += strspn(cmd, whitespace);
423 if (strcmp(cmd, "") == 0) {
424 sway_log(L_INFO, "Ignoring empty command.");
425 continue;
426 }
427 sway_log(L_INFO, "Handling command '%s'", cmd);
428 //TODO better handling of argv
429 int argc;
430 char **argv = split_args(cmd, &argc);
431 if (strcmp(argv[0], "exec") != 0) {
432 int i;
433 for (i = 1; i < argc; ++i) {
434 if (*argv[i] == '\"' || *argv[i] == '\'') {
435 strip_quotes(argv[i]);
436 }
437 }
438 }
439 struct cmd_handler *handler = find_handler(argv[0], CMD_BLOCK_END);
440 if (!handler) {
441 if (results) {
442 free_cmd_results(results);
443 }
444 results = cmd_results_new(CMD_INVALID, cmd, "Unknown/invalid command");
445 free_argv(argc, argv);
446 goto cleanup;
447 }
448 if (!(get_command_policy_mask(argv[0]) & context)) {
449 if (results) {
450 free_cmd_results(results);
451 }
452 results = cmd_results_new(CMD_INVALID, cmd,
453 "Permission denied for %s via %s", cmd,
454 command_policy_str(context));
455 free_argv(argc, argv);
456 goto cleanup;
457 }
458 int i = 0;
459 do {
460 if (!containers) {
461 current_container = get_focused_container(&root_container);
462 } else if (containers->length == 0) {
463 if (results) {
464 free_cmd_results(results);
465 }
466 results = cmd_results_new(CMD_FAILURE, argv[0], "No matching container");
467 goto cleanup;
468 } else {
469 current_container = (swayc_t *)containers->items[i];
470 }
471 sway_log(L_INFO, "Running on container '%s'", current_container->name);
472
473 struct cmd_results *res = handler->handle(argc-1, argv+1);
474 if (res->status != CMD_SUCCESS) {
475 free_argv(argc, argv);
476 if (results) {
477 free_cmd_results(results);
478 }
479 results = res;
480 goto cleanup;
481 }
482 free_cmd_results(res);
483 ++i;
484 } while(containers && i < containers->length);
485
486 free_argv(argc, argv);
487 } while(cmdlist);
488
489 if (containers) {
490 list_free(containers);
491 containers = NULL;
492 }
493 } while(head);
494 cleanup:
495 free(exec);
496 if (containers) {
497 free(containers);
498 }
499 if (!results) {
500 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
501 }
502 return results;
503}
504
505// this is like handle_command above, except:
506// 1) it ignores empty commands (empty lines)
507// 2) it does variable substitution
508// 3) it doesn't split commands (because the multiple commands are supposed to
509// be chained together)
510// 4) handle_command handles all state internally while config_command has some
511// state handled outside (notably the block mode, in read_config)
512struct cmd_results *config_command(char *exec, enum cmd_status block) {
513 struct cmd_results *results = NULL;
514 int argc;
515 char **argv = split_args(exec, &argc);
516 if (!argc) {
517 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
518 goto cleanup;
519 }
520
521 sway_log(L_INFO, "handling config command '%s'", exec);
522 // Endblock
523 if (**argv == '}') {
524 results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);
525 goto cleanup;
526 }
527 struct cmd_handler *handler = find_handler(argv[0], block);
528 if (!handler) {
529 char *input = argv[0] ? argv[0] : "(empty)";
530 results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command");
531 goto cleanup;
532 }
533 int i;
534 // Var replacement, for all but first argument of set
535 for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
536 argv[i] = do_var_replacement(argv[i]);
537 unescape_string(argv[i]);
538 }
539 /* Strip quotes for first argument.
540 * TODO This part needs to be handled much better */
541 if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
542 strip_quotes(argv[1]);
543 }
544 if (handler->handle) {
545 results = handler->handle(argc-1, argv+1);
546 } else {
547 results = cmd_results_new(CMD_INVALID, argv[0], "This command is shimmed, but unimplemented");
548 }
549
550cleanup:
551 free_argv(argc, argv);
552 return results;
553}
554
555struct cmd_results *config_commands_command(char *exec) {
556 struct cmd_results *results = NULL;
557 int argc;
558 char **argv = split_args(exec, &argc);
559 if (!argc) {
560 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
561 goto cleanup;
562 }
563
564 // Find handler for the command this is setting a policy for
565 char *cmd = argv[0];
566
567 if (strcmp(cmd, "}") == 0) {
568 results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);
569 goto cleanup;
570 }
571
572 struct cmd_handler *handler = find_handler(cmd, CMD_BLOCK_END);
573 if (!handler && strcmp(cmd, "*") != 0) {
574 char *input = cmd ? cmd : "(empty)";
575 results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command");
576 goto cleanup;
577 }
578
579 enum command_context context = 0;
580
581 struct {
582 char *name;
583 enum command_context context;
584 } context_names[] = {
585 { "config", CONTEXT_CONFIG },
586 { "binding", CONTEXT_BINDING },
587 { "ipc", CONTEXT_IPC },
588 { "criteria", CONTEXT_CRITERIA },
589 { "all", CONTEXT_ALL },
590 };
591
592 for (int i = 1; i < argc; ++i) {
593 size_t j;
594 for (j = 0; j < sizeof(context_names) / sizeof(context_names[0]); ++j) {
595 if (strcmp(context_names[j].name, argv[i]) == 0) {
596 break;
597 }
598 }
599 if (j == sizeof(context_names) / sizeof(context_names[0])) {
600 results = cmd_results_new(CMD_INVALID, cmd,
601 "Invalid command context %s", argv[i]);
602 goto cleanup;
603 }
604 context |= context_names[j].context;
605 }
606
607 struct command_policy *policy = NULL;
608 for (int i = 0; i < config->command_policies->length; ++i) {
609 struct command_policy *p = config->command_policies->items[i];
610 if (strcmp(p->command, cmd) == 0) {
611 policy = p;
612 break;
613 }
614 }
615 if (!policy) {
616 policy = alloc_command_policy(cmd);
617 sway_assert(policy, "Unable to allocate security policy");
618 if (policy) {
619 list_add(config->command_policies, policy);
620 }
621 }
622 policy->context = context;
623
624 sway_log(L_INFO, "Set command policy for %s to %d",
625 policy->command, policy->context);
626
627 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
628
629cleanup:
630 free_argv(argc, argv);
631 return results;
632}
633
634struct cmd_results *cmd_results_new(enum cmd_status status, const char* input, const char *format, ...) {
635 struct cmd_results *results = malloc(sizeof(struct cmd_results));
636 if (!results) {
637 sway_log(L_ERROR, "Unable to allocate command results");
638 return NULL;
639 }
640 results->status = status;
641 if (input) {
642 results->input = strdup(input); // input is the command name
643 } else {
644 results->input = NULL;
645 }
646 if (format) {
647 char *error = malloc(256);
648 va_list args;
649 va_start(args, format);
650 if (error) {
651 vsnprintf(error, 256, format, args);
652 }
653 va_end(args);
654 results->error = error;
655 } else {
656 results->error = NULL;
657 }
658 return results;
659}
660
661void free_cmd_results(struct cmd_results *results) {
662 if (results->input) {
663 free(results->input);
664 }
665 if (results->error) {
666 free(results->error);
667 }
668 free(results);
669}
670
671const char *cmd_results_to_json(struct cmd_results *results) {
672 json_object *result_array = json_object_new_array();
673 json_object *root = json_object_new_object();
674 json_object_object_add(root, "success", json_object_new_boolean(results->status == CMD_SUCCESS));
675 if (results->input) {
676 json_object_object_add(root, "input", json_object_new_string(results->input));
677 }
678 if (results->error) {
679 json_object_object_add(root, "error", json_object_new_string(results->error));
680 }
681 json_object_array_add(result_array, root);
682 const char *json = json_object_to_json_string(result_array);
683 free(result_array);
684 free(root);
685 return json;
686}