aboutsummaryrefslogtreecommitdiffstats
path: root/sway/commands
diff options
context:
space:
mode:
authorLibravatar Brian Ashworth <bosrsf04@gmail.com>2018-05-30 13:20:02 -0400
committerLibravatar Brian Ashworth <bosrsf04@gmail.com>2018-06-02 08:07:44 -0400
commit7c810dc344c28d1876c5ee158cb0806289d0f813 (patch)
treedbe756bceca42ea6f9a6cf5e5771037417bb64c3 /sway/commands
parentMerge pull request #2080 from frsfnrrg/keyboard-remodeling (diff)
downloadsway-7c810dc344c28d1876c5ee158cb0806289d0f813.tar.gz
sway-7c810dc344c28d1876c5ee158cb0806289d0f813.tar.zst
sway-7c810dc344c28d1876c5ee158cb0806289d0f813.zip
Make command block implementation generic
Diffstat (limited to 'sway/commands')
-rw-r--r--sway/commands/bar.c118
-rw-r--r--sway/commands/bar/colors.c26
-rw-r--r--sway/commands/bar/id.c3
-rw-r--r--sway/commands/input.c103
-rw-r--r--sway/commands/mode.c28
-rw-r--r--sway/commands/seat.c61
6 files changed, 180 insertions, 159 deletions
diff --git a/sway/commands/bar.c b/sway/commands/bar.c
index ff111163..358b3893 100644
--- a/sway/commands/bar.c
+++ b/sway/commands/bar.c
@@ -1,3 +1,4 @@
1#define _XOPEN_SOURCE 500
1#include <string.h> 2#include <string.h>
2#include <strings.h> 3#include <strings.h>
3#include <wlr/util/log.h> 4#include <wlr/util/log.h>
@@ -5,53 +6,110 @@
5#include "sway/config.h" 6#include "sway/config.h"
6#include "util.h" 7#include "util.h"
7 8
9// Must be in alphabetical order for bsearch
10static struct cmd_handler bar_handlers[] = {
11 { "activate_button", bar_cmd_activate_button },
12 { "binding_mode_indicator", bar_cmd_binding_mode_indicator },
13 { "bindsym", bar_cmd_bindsym },
14 { "colors", bar_cmd_colors },
15 { "context_button", bar_cmd_context_button },
16 { "font", bar_cmd_font },
17 { "height", bar_cmd_height },
18 { "hidden_state", bar_cmd_hidden_state },
19 { "icon_theme", bar_cmd_icon_theme },
20 { "id", bar_cmd_id },
21 { "mode", bar_cmd_mode },
22 { "modifier", bar_cmd_modifier },
23 { "output", bar_cmd_output },
24 { "pango_markup", bar_cmd_pango_markup },
25 { "position", bar_cmd_position },
26 { "secondary_button", bar_cmd_secondary_button },
27 { "separator_symbol", bar_cmd_separator_symbol },
28 { "status_command", bar_cmd_status_command },
29 { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers },
30 { "swaybar_command", bar_cmd_swaybar_command },
31 { "tray_output", bar_cmd_tray_output },
32 { "tray_padding", bar_cmd_tray_padding },
33 { "workspace_buttons", bar_cmd_workspace_buttons },
34 { "wrap_scroll", bar_cmd_wrap_scroll },
35};
36
37// Must be in alphabetical order for bsearch
38static struct cmd_handler bar_config_handlers[] = {
39 { "hidden_state", bar_cmd_hidden_state },
40 { "mode", bar_cmd_mode }
41};
42
8struct cmd_results *cmd_bar(int argc, char **argv) { 43struct cmd_results *cmd_bar(int argc, char **argv) {
9 struct cmd_results *error = NULL; 44 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "bar", EXPECTED_AT_LEAST, 1))) { 45 if ((error = checkarg(argc, "bar", EXPECTED_AT_LEAST, 1))) {
11 return error; 46 return error;
12 } 47 }
13 48
14 if (config->reading && strcmp("{", argv[0]) != 0) { 49 if (!config->reading) {
15 return cmd_results_new(CMD_INVALID, "bar", 50 if (!find_handler(argv[0], bar_config_handlers,
16 "Expected '{' at start of bar config definition."); 51 sizeof(bar_config_handlers))) {
52 return cmd_results_new(CMD_FAILURE, "bar",
53 "Can only be used in config file.");
54 }
55 return subcommand(argv, argc, bar_config_handlers,
56 sizeof(bar_config_handlers));
17 } 57 }
18 58
19 if (!config->reading) { 59 if (argc > 1) {
20 if (argc > 1) { 60 struct bar_config *bar = NULL;
21 if (strcasecmp("mode", argv[0]) == 0) { 61 if (!find_handler(argv[0], bar_handlers, sizeof(bar_handlers))
22 return bar_cmd_mode(argc-1, argv + 1); 62 && find_handler(argv[1], bar_handlers, sizeof(bar_handlers))) {
63 for (int i = 0; i < config->bars->length; ++i) {
64 struct bar_config *item = config->bars->items[i];
65 if (strcmp(item->id, argv[0]) == 0) {
66 wlr_log(L_DEBUG, "Selecting bar: %s", argv[0]);
67 bar = item;
68 break;
69 }
23 } 70 }
71 if (!bar) {
72 wlr_log(L_DEBUG, "Creating bar: %s", argv[0]);
73 bar = default_bar_config();
74 if (!bar) {
75 return cmd_results_new(CMD_FAILURE, "bar",
76 "Unable to allocate bar state");
77 }
24 78
25 if (strcasecmp("hidden_state", argv[0]) == 0) { 79 bar->id = strdup(argv[0]);
26 return bar_cmd_hidden_state(argc-1, argv + 1);
27 } 80 }
81 config->current_bar = bar;
82 ++argv; --argc;
28 } 83 }
29 return cmd_results_new(CMD_FAILURE, "bar", "Can only be used in config file.");
30 } 84 }
31 85
32 // Create new bar with default values 86 if (!config->current_bar) {
33 struct bar_config *bar = default_bar_config(); 87 // Create new bar with default values
34 if (!bar) { 88 struct bar_config *bar = default_bar_config();
35 return cmd_results_new(CMD_FAILURE, "bar", "Unable to allocate bar state"); 89 if (!bar) {
36 } 90 return cmd_results_new(CMD_FAILURE, "bar",
91 "Unable to allocate bar state");
92 }
37 93
38 // set bar id 94 // set bar id
39 for (int i = 0; i < config->bars->length; ++i) { 95 for (int i = 0; i < config->bars->length; ++i) {
40 if (bar == config->bars->items[i]) { 96 if (bar == config->bars->items[i]) {
41 const int len = 5 + numlen(i); // "bar-" + i + \0 97 const int len = 5 + numlen(i); // "bar-" + i + \0
42 bar->id = malloc(len * sizeof(char)); 98 bar->id = malloc(len * sizeof(char));
43 if (bar->id) { 99 if (bar->id) {
44 snprintf(bar->id, len, "bar-%d", i); 100 snprintf(bar->id, len, "bar-%d", i);
45 } else { 101 } else {
46 return cmd_results_new(CMD_FAILURE, 102 return cmd_results_new(CMD_FAILURE,
47 "bar", "Unable to allocate bar ID"); 103 "bar", "Unable to allocate bar ID");
104 }
105 break;
48 } 106 }
49 break;
50 } 107 }
108
109 // Set current bar
110 config->current_bar = bar;
111 wlr_log(L_DEBUG, "Creating bar %s", bar->id);
51 } 112 }
52 113
53 // Set current bar 114 return subcommand(argv, argc, bar_handlers, sizeof(bar_handlers));
54 config->current_bar = bar;
55 wlr_log(L_DEBUG, "Configuring bar %s", bar->id);
56 return cmd_results_new(CMD_BLOCK_BAR, NULL, NULL);
57} 115}
diff --git a/sway/commands/bar/colors.c b/sway/commands/bar/colors.c
index 17ba9b7c..6d3c09b8 100644
--- a/sway/commands/bar/colors.c
+++ b/sway/commands/bar/colors.c
@@ -1,6 +1,21 @@
1#include <string.h> 1#include <string.h>
2#include "sway/commands.h" 2#include "sway/commands.h"
3 3
4// Must be in alphabetical order for bsearch
5static struct cmd_handler bar_colors_handlers[] = {
6 { "active_workspace", bar_colors_cmd_active_workspace },
7 { "background", bar_colors_cmd_background },
8 { "binding_mode", bar_colors_cmd_binding_mode },
9 { "focused_background", bar_colors_cmd_focused_background },
10 { "focused_separator", bar_colors_cmd_focused_separator },
11 { "focused_statusline", bar_colors_cmd_focused_statusline },
12 { "focused_workspace", bar_colors_cmd_focused_workspace },
13 { "inactive_workspace", bar_colors_cmd_inactive_workspace },
14 { "separator", bar_colors_cmd_separator },
15 { "statusline", bar_colors_cmd_statusline },
16 { "urgent_workspace", bar_colors_cmd_urgent_workspace },
17};
18
4static struct cmd_results *parse_single_color(char **color, 19static struct cmd_results *parse_single_color(char **color,
5 const char *cmd_name, int argc, char **argv) { 20 const char *cmd_name, int argc, char **argv) {
6 struct cmd_results *error = NULL; 21 struct cmd_results *error = NULL;
@@ -37,15 +52,8 @@ static struct cmd_results *parse_three_colors(char ***colors,
37} 52}
38 53
39struct cmd_results *bar_cmd_colors(int argc, char **argv) { 54struct cmd_results *bar_cmd_colors(int argc, char **argv) {
40 struct cmd_results *error = NULL; 55 return subcommand(argv, argc, bar_colors_handlers,
41 if ((error = checkarg(argc, "colors", EXPECTED_EQUAL_TO, 1))) { 56 sizeof(bar_colors_handlers));
42 return error;
43 }
44 if (strcmp("{", argv[0]) != 0) {
45 return cmd_results_new(CMD_INVALID, "colors",
46 "Expected '{' at the start of colors config definition.");
47 }
48 return cmd_results_new(CMD_BLOCK_BAR_COLORS, NULL, NULL);
49} 57}
50 58
51struct cmd_results *bar_colors_cmd_active_workspace(int argc, char **argv) { 59struct cmd_results *bar_colors_cmd_active_workspace(int argc, char **argv) {
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c
index c1e56f03..6ce86fef 100644
--- a/sway/commands/bar/id.c
+++ b/sway/commands/bar/id.c
@@ -11,6 +11,9 @@ struct cmd_results *bar_cmd_id(int argc, char **argv) {
11 11
12 const char *name = argv[0]; 12 const char *name = argv[0];
13 const char *oldname = config->current_bar->id; 13 const char *oldname = config->current_bar->id;
14 if (strcmp(name, oldname) == 0) {
15 return cmd_results_new(CMD_SUCCESS, NULL, NULL); // NOP
16 }
14 // check if id is used by a previously defined bar 17 // check if id is used by a previously defined bar
15 for (int i = 0; i < config->bars->length; ++i) { 18 for (int i = 0; i < config->bars->length; ++i) {
16 struct bar_config *find = config->bars->items[i]; 19 struct bar_config *find = config->bars->items[i];
diff --git a/sway/commands/input.c b/sway/commands/input.c
index eeb4ee75..972160e2 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -3,85 +3,50 @@
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/input/input-manager.h" 4#include "sway/input/input-manager.h"
5#include "log.h" 5#include "log.h"
6#include "stringop.h"
7
8// must be in order for the bsearch
9static struct cmd_handler input_handlers[] = {
10 { "accel_profile", input_cmd_accel_profile },
11 { "click_method", input_cmd_click_method },
12 { "drag_lock", input_cmd_drag_lock },
13 { "dwt", input_cmd_dwt },
14 { "events", input_cmd_events },
15 { "left_handed", input_cmd_left_handed },
16 { "map_from_region", input_cmd_map_from_region },
17 { "map_to_output", input_cmd_map_to_output },
18 { "middle_emulation", input_cmd_middle_emulation },
19 { "natural_scroll", input_cmd_natural_scroll },
20 { "pointer_accel", input_cmd_pointer_accel },
21 { "repeat_delay", input_cmd_repeat_delay },
22 { "repeat_rate", input_cmd_repeat_rate },
23 { "scroll_method", input_cmd_scroll_method },
24 { "tap", input_cmd_tap },
25 { "xkb_layout", input_cmd_xkb_layout },
26 { "xkb_model", input_cmd_xkb_model },
27 { "xkb_options", input_cmd_xkb_options },
28 { "xkb_rules", input_cmd_xkb_rules },
29 { "xkb_variant", input_cmd_xkb_variant },
30};
6 31
7struct cmd_results *cmd_input(int argc, char **argv) { 32struct cmd_results *cmd_input(int argc, char **argv) {
8 struct cmd_results *error = NULL; 33 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 2))) { 34 if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 1))) {
10 return error; 35 return error;
11 } 36 }
12 37
13 if (config->reading && strcmp("{", argv[1]) == 0) { 38 wlr_log(L_DEBUG, "entering input block: %s", argv[0]);
14 free_input_config(config->handler_context.input_config);
15 config->handler_context.input_config = new_input_config(argv[0]);
16 if (!config->handler_context.input_config) {
17 return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config");
18 }
19 wlr_log(L_DEBUG, "entering input block: %s", argv[0]);
20 return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL);
21 }
22
23 if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 3))) {
24 return error;
25 }
26 39
27 bool has_context = (config->handler_context.input_config != NULL); 40 config->handler_context.input_config = new_input_config(argv[0]);
28 if (!has_context) { 41 if (!config->handler_context.input_config) {
29 // caller did not give a context so create one just for this command 42 return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config");
30 config->handler_context.input_config = new_input_config(argv[0]);
31 if (!config->handler_context.input_config) {
32 return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config");
33 }
34 } 43 }
35 44
36 int argc_new = argc-2; 45 struct cmd_results *res = subcommand(argv + 1, argc - 1, input_handlers,
37 char **argv_new = argv+2; 46 sizeof(input_handlers));
38 47
39 struct cmd_results *res; 48 free_input_config(config->handler_context.input_config);
40 if (strcasecmp("accel_profile", argv[1]) == 0) { 49 config->handler_context.input_config = NULL;
41 res = input_cmd_accel_profile(argc_new, argv_new);
42 } else if (strcasecmp("click_method", argv[1]) == 0) {
43 res = input_cmd_click_method(argc_new, argv_new);
44 } else if (strcasecmp("drag_lock", argv[1]) == 0) {
45 res = input_cmd_drag_lock(argc_new, argv_new);
46 } else if (strcasecmp("dwt", argv[1]) == 0) {
47 res = input_cmd_dwt(argc_new, argv_new);
48 } else if (strcasecmp("events", argv[1]) == 0) {
49 res = input_cmd_events(argc_new, argv_new);
50 } else if (strcasecmp("left_handed", argv[1]) == 0) {
51 res = input_cmd_left_handed(argc_new, argv_new);
52 } else if (strcasecmp("middle_emulation", argv[1]) == 0) {
53 res = input_cmd_middle_emulation(argc_new, argv_new);
54 } else if (strcasecmp("natural_scroll", argv[1]) == 0) {
55 res = input_cmd_natural_scroll(argc_new, argv_new);
56 } else if (strcasecmp("pointer_accel", argv[1]) == 0) {
57 res = input_cmd_pointer_accel(argc_new, argv_new);
58 } else if (strcasecmp("repeat_delay", argv[1]) == 0) {
59 res = input_cmd_repeat_delay(argc_new, argv_new);
60 } else if (strcasecmp("repeat_rate", argv[1]) == 0) {
61 res = input_cmd_repeat_rate(argc_new, argv_new);
62 } else if (strcasecmp("scroll_method", argv[1]) == 0) {
63 res = input_cmd_scroll_method(argc_new, argv_new);
64 } else if (strcasecmp("tap", argv[1]) == 0) {
65 res = input_cmd_tap(argc_new, argv_new);
66 } else if (strcasecmp("xkb_layout", argv[1]) == 0) {
67 res = input_cmd_xkb_layout(argc_new, argv_new);
68 } else if (strcasecmp("xkb_model", argv[1]) == 0) {
69 res = input_cmd_xkb_model(argc_new, argv_new);
70 } else if (strcasecmp("xkb_options", argv[1]) == 0) {
71 res = input_cmd_xkb_options(argc_new, argv_new);
72 } else if (strcasecmp("xkb_rules", argv[1]) == 0) {
73 res = input_cmd_xkb_rules(argc_new, argv_new);
74 } else if (strcasecmp("xkb_variant", argv[1]) == 0) {
75 res = input_cmd_xkb_variant(argc_new, argv_new);
76 } else {
77 res = cmd_results_new(CMD_INVALID, "input <device>", "Unknown command %s", argv[1]);
78 }
79
80 if (!has_context) {
81 // clean up the context we created earlier
82 free_input_config(config->handler_context.input_config);
83 config->handler_context.input_config = NULL;
84 }
85 50
86 return res; 51 return res;
87} 52}
diff --git a/sway/commands/mode.c b/sway/commands/mode.c
index c30a8bac..31d0f251 100644
--- a/sway/commands/mode.c
+++ b/sway/commands/mode.c
@@ -7,6 +7,13 @@
7#include "sway/ipc-server.h" 7#include "sway/ipc-server.h"
8#include "list.h" 8#include "list.h"
9#include "log.h" 9#include "log.h"
10#include "stringop.h"
11
12// Must be in order for the bsearch
13static struct cmd_handler mode_handlers[] = {
14 { "bindcode", cmd_bindcode },
15 { "bindsym", cmd_bindsym }
16};
10 17
11struct cmd_results *cmd_mode(int argc, char **argv) { 18struct cmd_results *cmd_mode(int argc, char **argv) {
12 struct cmd_results *error = NULL; 19 struct cmd_results *error = NULL;
@@ -14,12 +21,12 @@ struct cmd_results *cmd_mode(int argc, char **argv) {
14 return error; 21 return error;
15 } 22 }
16 23
17 const char *mode_name = argv[0]; 24 if (argc > 1 && !config->reading) {
18 bool new_mode = (argc == 2 && strcmp(argv[1], "{") == 0);
19 if (new_mode && !config->reading) {
20 return cmd_results_new(CMD_FAILURE, 25 return cmd_results_new(CMD_FAILURE,
21 "mode", "Can only be used in config file."); 26 "mode", "Can only be used in config file.");
22 } 27 }
28
29 const char *mode_name = argv[0];
23 struct sway_mode *mode = NULL; 30 struct sway_mode *mode = NULL;
24 // Find mode 31 // Find mode
25 for (int i = 0; i < config->modes->length; ++i) { 32 for (int i = 0; i < config->modes->length; ++i) {
@@ -30,7 +37,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) {
30 } 37 }
31 } 38 }
32 // Create mode if it doesn't exist 39 // Create mode if it doesn't exist
33 if (!mode && new_mode) { 40 if (!mode && argc > 1) {
34 mode = calloc(1, sizeof(struct sway_mode)); 41 mode = calloc(1, sizeof(struct sway_mode));
35 if (!mode) { 42 if (!mode) {
36 return cmd_results_new(CMD_FAILURE, 43 return cmd_results_new(CMD_FAILURE,
@@ -46,14 +53,21 @@ struct cmd_results *cmd_mode(int argc, char **argv) {
46 "mode", "Unknown mode `%s'", mode_name); 53 "mode", "Unknown mode `%s'", mode_name);
47 return error; 54 return error;
48 } 55 }
49 if ((config->reading && new_mode) || (!config->reading && !new_mode)) { 56 if ((config->reading && argc > 1) || (!config->reading && argc == 1)) {
50 wlr_log(L_DEBUG, "Switching to mode `%s'",mode->name); 57 wlr_log(L_DEBUG, "Switching to mode `%s'",mode->name);
51 } 58 }
52 // Set current mode 59 // Set current mode
53 config->current_mode = mode; 60 config->current_mode = mode;
54 if (!new_mode) { 61 if (argc == 1) {
55 // trigger IPC mode event 62 // trigger IPC mode event
56 ipc_event_mode(config->current_mode->name); 63 ipc_event_mode(config->current_mode->name);
64 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
57 } 65 }
58 return cmd_results_new(new_mode ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL); 66
67 // Create binding
68 struct cmd_results *result = subcommand(argv + 1, argc - 1, mode_handlers,
69 sizeof(mode_handlers));
70 config->current_mode = config->modes->items[0];
71
72 return result;
59} 73}
diff --git a/sway/commands/seat.c b/sway/commands/seat.c
index 5916015f..6080bf64 100644
--- a/sway/commands/seat.c
+++ b/sway/commands/seat.c
@@ -3,59 +3,32 @@
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/input/input-manager.h" 4#include "sway/input/input-manager.h"
5#include "log.h" 5#include "log.h"
6#include "stringop.h"
7
8// must be in order for the bsearch
9static struct cmd_handler seat_handlers[] = {
10 { "attach", seat_cmd_attach },
11 { "cursor", seat_cmd_cursor },
12 { "fallback", seat_cmd_fallback },
13};
6 14
7struct cmd_results *cmd_seat(int argc, char **argv) { 15struct cmd_results *cmd_seat(int argc, char **argv) {
8 struct cmd_results *error = NULL; 16 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 2))) { 17 if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 1))) {
10 return error; 18 return error;
11 } 19 }
12 20
13 if (config->reading && strcmp("{", argv[1]) == 0) { 21 config->handler_context.seat_config = new_seat_config(argv[0]);
14 free_seat_config(config->handler_context.seat_config); 22 if (!config->handler_context.seat_config) {
15 config->handler_context.seat_config = new_seat_config(argv[0]); 23 return cmd_results_new(CMD_FAILURE, NULL,
16 if (!config->handler_context.seat_config) { 24 "Couldn't allocate config");
17 return cmd_results_new(CMD_FAILURE, NULL,
18 "Couldn't allocate config");
19 }
20 wlr_log(L_DEBUG, "entering seat block: %s", argv[0]);
21 return cmd_results_new(CMD_BLOCK_SEAT, NULL, NULL);
22 } 25 }
23 26
24 if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 3))) { 27 struct cmd_results *res = subcommand(argv + 1, argc - 1, seat_handlers,
25 return error; 28 sizeof(seat_handlers));
26 }
27
28 bool has_context = (config->handler_context.seat_config != NULL);
29 if (!has_context) {
30 config->handler_context.seat_config = new_seat_config(argv[0]);
31 if (!config->handler_context.seat_config) {
32 return cmd_results_new(CMD_FAILURE, NULL,
33 "Couldn't allocate config");
34 }
35 }
36 29
37 int argc_new = argc-2; 30 free_seat_config(config->handler_context.seat_config);
38 char **argv_new = argv+2; 31 config->handler_context.seat_config = NULL;
39
40 struct cmd_results *res;
41 if (strcasecmp("attach", argv[1]) == 0) {
42 res = seat_cmd_attach(argc_new, argv_new);
43 } else if (strcasecmp("cursor", argv[1]) == 0) {
44 res = seat_cmd_cursor(argc_new, argv_new);
45 } else if (strcasecmp("fallback", argv[1]) == 0) {
46 res = seat_cmd_fallback(argc_new, argv_new);
47 } else {
48 res =
49 cmd_results_new(CMD_INVALID,
50 "seat <name>", "Unknown command %s",
51 argv[1]);
52 }
53
54 if (!has_context) {
55 // clean up the context we created earlier
56 free_seat_config(config->handler_context.seat_config);
57 config->handler_context.seat_config = NULL;
58 }
59 32
60 return res; 33 return res;
61} 34}