diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2016-01-05 14:56:11 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2016-01-05 14:56:11 +0200 |
commit | 32a337f3b35c51ceb1d9320c7ff67fa1d5726c2a (patch) | |
tree | 098d41b0d07d62144f8398dc3c5f80a295369f98 | |
parent | 2c03055b8ed023e7d8d65f2dbd51cd128040fcfb (diff) | |
download | pies-32a337f3b35c51ceb1d9320c7ff67fa1d5726c2a.tar.gz pies-32a337f3b35c51ceb1d9320c7ff67fa1d5726c2a.tar.bz2 |
Select components to operate upon using conditional expression
* grecs: Upgrade.
* src/ctl.c (restab): Split /programs in two endpoints.
(eval_env): New members allowed_state, fun, total_count,
success_count, and noperm_count.
(pcond_type): pcond_and and pcond_or can take arbitrary
number of arguments. All uses updated.
(selector): Rewrite.
* src/piesctl.c: All commands that operate on components take
conditional expression as argument.
(pcond_parse_and)
(pcond_parse_or): Optimize consecutive operations.
m--------- | grecs | 0 | ||||
-rw-r--r-- | src/ctl.c | 288 | ||||
-rw-r--r-- | src/piesctl.c | 234 |
3 files changed, 365 insertions, 157 deletions
diff --git a/grecs b/grecs -Subproject 3b73967c62da68d865f32ca91c8407e65b8ddc0 +Subproject 384b8c088c290f06f70835402f903627c67de6f @@ -888,8 +888,10 @@ method_decode (char const *method) static void res_instance (struct ctlio *, enum http_method, char const *, struct json_value *); -static void res_programs (struct ctlio *, enum http_method, char const *, +static void res_programs_select (struct ctlio *, enum http_method, char const *, struct json_value *); +static void res_programs_component (struct ctlio *, enum http_method, + char const *, struct json_value *); struct ctlio_resource { @@ -903,7 +905,10 @@ struct ctlio_resource static struct ctlio_resource restab[] = { #define S(s) #s, (sizeof (#s)-1) { S(/instance), CTL_ADMIN_STATE, res_instance }, - { S(/programs), CTL_ADMIN_STATE|CTL_USER_STATE, res_programs }, + { S(/programs/select), CTL_ADMIN_STATE|CTL_USER_STATE, + res_programs_select }, + { S(/programs/component), CTL_ADMIN_STATE|CTL_USER_STATE, + res_programs_component }, { NULL } #undef S }; @@ -931,7 +936,10 @@ static char * json_extract (char *uri) { char *p = strrchr (uri, '/'); - if (p && strchr ("\"{[", p[1])) + if (p && (strchr ("\"{[", p[1]) + || strcmp ("true", p + 1) == 0 + || strcmp ("false", p + 1) == 0 + || strcmp ("null", p + 1) == 0)) { *p++ = 0; return p; @@ -1313,8 +1321,13 @@ res_instance (struct ctlio *io, enum http_method meth, struct eval_env { struct ctlio *io; + int allowed_state; struct pcond_node *cond; + int (*fun) (struct json_value *, struct prog *); struct json_value *result; + size_t total_count; + size_t success_count; + size_t noperm_count; }; static int @@ -1390,6 +1403,7 @@ format_idx (struct json_value *obj, const char *name, unsigned i, enum pcond_type { + pcond_false, pcond_true, pcond_component, pcond_type, @@ -1409,8 +1423,11 @@ struct pcond_node enum pies_comp_mode mode; enum prog_status status; enum prog_type type; - struct pcond_node *unary; - struct pcond_node *binary[2]; + struct + { + size_t c; + struct pcond_node **v; + } arg; } v; }; @@ -1432,13 +1449,15 @@ pcond_node_free (struct pcond_node *node) switch (node->type) { case pcond_not: - pcond_node_free (node->v.unary); - break; - case pcond_and: case pcond_or: - pcond_node_free (node->v.binary[0]); - pcond_node_free (node->v.binary[1]); + { + size_t i; + + for (i = 0; i < node->v.arg.c; i++) + pcond_node_free (node->v.arg.v[i]); + } + grecs_free (node->v.arg.v); break; default: @@ -1450,6 +1469,8 @@ pcond_node_free (struct pcond_node *node) static int pcond_eval (struct pcond_node *node, struct prog *p) { + size_t i; + if (!node) return 0; switch (node->type) @@ -1457,6 +1478,9 @@ pcond_eval (struct pcond_node *node, struct prog *p) case pcond_true: return 1; + case pcond_false: + return 0; + case pcond_component: return strcmp (p->tag, node->v.tag) == 0; @@ -1470,17 +1494,19 @@ pcond_eval (struct pcond_node *node, struct prog *p) return IS_COMPONENT (p) && p->v.p.status == node->v.status; case pcond_not: - return !pcond_eval (node->v.unary, p); + return !pcond_eval (node->v.arg.v[0], p); case pcond_and: - if (!pcond_eval (node->v.binary[0], p)) + for (i = 0; i < node->v.arg.c; i++) + if (!pcond_eval (node->v.arg.v[i], p)) return 0; - return pcond_eval (node->v.binary[1], p); + return 1; case pcond_or: - if (pcond_eval (node->v.binary[0], p)) + for (i = 0; i < node->v.arg.c; i++) + if (pcond_eval (node->v.arg.v[i], p)) return 1; - return pcond_eval (node->v.binary[1], p); + return 0; default: abort (); @@ -1560,26 +1586,36 @@ static int pcond_conv_not (struct pcond_node *node, struct json_value *arg, struct ctlio *io) { - return json_to_pcond (io, arg, &node->v.unary); + node->v.arg.v = grecs_calloc (1, sizeof (node->v.arg.v[0])); + node->v.arg.c = 1; + return json_to_pcond (io, arg, &node->v.arg.v[0]); } static int pcond_conv_binary (struct pcond_node *node, struct json_value *arg, struct ctlio *io) { - struct json_value *v; + size_t i, n; if (arg->type != json_arr) { ctlio_reply (io, 400, "arguments to binary opcode must be array"); return -1; } - json_array_get (arg, 0, &v); - if (json_to_pcond (io, v, &node->v.binary[0])) - return -1; - json_array_get (arg, 1, &v); - if (json_to_pcond (io, v, &node->v.binary[1])) + + n = json_array_size (arg); + node->v.arg.v = grecs_calloc (n, sizeof (node->v.arg.v[0])); + node->v.arg.c = n; + + for (i = 0; i < n; i++) + { + struct json_value *v; + if (json_array_get (arg, i, &v) == 0) + { + if (json_to_pcond (io, v, &node->v.arg.v[i])) return -1; + } + } return 0; } @@ -1590,6 +1626,7 @@ struct pcond_conv { static struct pcond_conv pcond_conv[] = { [pcond_true] = { "true", NULL }, + [pcond_false] = { "false", NULL }, [pcond_component] = { "component", pcond_conv_component }, [pcond_type] = { "type", pcond_conv_type }, [pcond_mode] = { "mode", pcond_conv_mode }, @@ -1669,10 +1706,7 @@ json_to_pcond (struct ctlio *io, struct json_value *val, break; case json_bool: - if (val->v.b) - *pnode = pcond_node_alloc (pcond_true); - else - *pnode = NULL; + *pnode = pcond_node_alloc (val->v.b ? pcond_true : pcond_false); break; case json_number: @@ -1687,26 +1721,34 @@ json_to_pcond (struct ctlio *io, struct json_value *val, return 0; } -static struct json_value *prog_serialize (struct prog *prog); - static int selector (struct prog *prog, void *data) { struct eval_env *env = data; - if (auth_prog (prog, env->io) && pcond_eval (env->cond, prog)) - json_array_append (env->result, prog_serialize (prog)); + if (pcond_eval (env->cond, prog)) + { + if (auth_prog (prog, env->io) & env->allowed_state) + { + struct json_value *status = json_new_object (); + json_object_set_string (status, "tag", prog->tag); + if (env->fun (status, prog) == 0) + env->success_count++; + json_array_append (env->result, status); + env->total_count++; + } + else + env->noperm_count++; + } return 0; } static struct json_value * -prog_serialize (struct prog *prog) +prog_serialize (struct json_value *ret, struct prog *prog) { - struct json_value *ret = json_reply_create (); struct json_value *v; size_t i; - json_object_set_string (ret, "tag", prog->tag); FORMAT_IDX (ret, "type", pies_type_str, prog->type); switch (prog->type) { @@ -1743,97 +1785,195 @@ prog_serialize (struct prog *prog) return ret; } -static void -component_stop (struct ctlio *io, struct prog *prog) +static int +fun_list (struct json_value *result, struct prog *prog) +{ + prog_serialize (result, prog); + return 0; +} + +static int +fun_stop (struct json_value *result, struct prog *prog) { if (prog->v.p.comp->flags & CF_DISABLED) - ctlio_reply (io, 409, "Already stopped"); + { + json_object_set_string (result, "status", "ER"); + json_object_set_string (result, "error_message", "already stopped"); + return 1; + } else { progman_stop_component (prog); prog->v.p.comp->flags |= CF_DISABLED; - ctlio_reply (io, 200, "Component stopped"); + json_object_set_string (result, "status", "OK"); } + return 0; } -static void -component_start (struct ctlio *io, struct prog *prog) +static int +fun_start (struct json_value *result, struct prog *prog) { if (prog->v.p.comp->flags & CF_DISABLED) { prog->v.p.comp->flags &= ~CF_DISABLED; prog->v.p.status = status_enabled; - kill (getpid (), SIGALRM); - ctlio_reply (io, 200, "Component started"); + json_object_set_string (result, "status", "OK"); } else - ctlio_reply (io, 409, "Already running"); + { + json_object_set_string (result, "status", "ER"); + json_object_set_string (result, "error_message", "already running"); + return 1; + } + return 0; } -static void -res_programs (struct ctlio *io, enum http_method meth, - char const *uri, struct json_value *json) +static int +fun_restart (struct json_value *result, struct prog *prog) { - if (meth == METH_GET) + progman_stop_component (prog); + json_object_set_string (result, "status", "OK"); + return 0; +} + +static void +select_and_run (struct ctlio *io, struct pcond_node *cond, + enum http_method meth) { struct eval_env env; + int wakeup = 0; + memset (&env, 0, sizeof (env)); env.io = io; - if (json_to_pcond (io, json, &env.cond)) - return; + env.cond = cond; + env.fun = NULL; + env.result = json_new_array (); + + switch (meth) + { + case METH_GET: + env.allowed_state = CTL_USER_STATE | CTL_ADMIN_STATE; + env.fun = fun_list; + break; + + case METH_DELETE: + env.allowed_state = CTL_ADMIN_STATE; + env.fun = fun_stop; + break; + + case METH_PUT: + env.allowed_state = CTL_ADMIN_STATE; + env.fun = fun_start; + wakeup = 1; + break; + + case METH_POST: + env.allowed_state = CTL_ADMIN_STATE; + env.fun = fun_restart; + wakeup = 1; + break; + + default: + ctlio_reply (io, 405, NULL); + } + + if (env.fun) + { env.result = json_new_array (); progman_foreach (selector, &env); - pcond_node_free (env.cond); + + if (env.success_count == 0 && env.noperm_count) + ctlio_reply (io, 403, NULL); + else + { + if (env.success_count && wakeup) + progman_wake_sleeping (1); io->output.reply = env.result; io->code = 200; } - else if (meth == METH_DELETE || meth == METH_PUT) + } +} + +static void +res_programs_select (struct ctlio *io, enum http_method meth, + char const *uri, struct json_value *json) { - if (!uri) - ctlio_reply (io, 404, NULL); - else + struct pcond_node *cond; + + if (uri) { - struct prog *prog = progman_locate (uri); - if (!prog) ctlio_reply (io, 404, NULL); - else if (auth_prog (prog, io) & CTL_ADMIN_STATE) + return; + } + + if (json_to_pcond (io, json, &cond)) + return; + select_and_run (io, cond, meth); + pcond_node_free (cond); +} + +static void +res_programs_component (struct ctlio *io, enum http_method meth, + char const *uri, struct json_value *json) { - if (!IS_COMPONENT (prog)) - ctlio_reply (io, 404, "Not a component"); - else if (meth == METH_DELETE) + struct pcond_node *node = pcond_node_alloc (pcond_type); + node->v.type = TYPE_COMPONENT; + + if (meth != METH_POST) { - component_stop (io, prog); - } - else + if (uri) { - component_start (io, prog); + struct pcond_node *np; + np = pcond_node_alloc (pcond_and); + np->v.arg.c = 2; + np->v.arg.v = grecs_calloc (np->v.arg.c, sizeof (np->v.arg.v[0])); + np->v.arg.v[0] = node; + np->v.arg.v[1] = pcond_node_alloc (pcond_component); + np->v.arg.v[1]->v.tag = (char*) uri + 1; + node = np; } } else - ctlio_reply (io, 403, NULL); - } - } - else if (meth == METH_POST) { if (json->type != json_arr) - { ctlio_reply (io, 400, NULL); - } else { size_t i, n; + struct pcond_node *or_cond, *np; n = json_array_size (json); + or_cond = pcond_node_alloc (pcond_or); + or_cond->v.arg.v = grecs_calloc (n, sizeof (or_cond->v.arg.v[0])); + or_cond->v.arg.c = n; + for (i = 0; i < n; i++) { struct json_value *v; if (json_array_get (json, i, &v) == 0 && v->type == json_string) - progman_stop_tag (v->v.s); + { + or_cond->v.arg.v[i] = pcond_node_alloc (pcond_component); + or_cond->v.arg.v[i]->v.tag = v->v.s; } - kill (getpid (), SIGALRM); + else + { + ctlio_reply (io, 400, "all elements must be string"); + pcond_node_free (or_cond); + pcond_node_free (node); + return; } - ctlio_reply (io, 200, "Done"); } - else - ctlio_reply (io, 405, NULL); + + np = pcond_node_alloc (pcond_and); + np->v.arg.c = 2; + np->v.arg.v = grecs_calloc (np->v.arg.c, sizeof (np->v.arg.v[0])); + np->v.arg.v[0] = node; + np->v.arg.v[1] = or_cond; + + node = np; + } + } + + select_and_run (io, node, meth); + pcond_node_free (node); } diff --git a/src/piesctl.c b/src/piesctl.c index 8c4e2e1..4b448ad 100644 --- a/src/piesctl.c +++ b/src/piesctl.c @@ -1228,15 +1228,6 @@ peek_token (struct pcond_parser_state *state) } static struct json_value * -json_new_array2 (struct json_value *a, struct json_value *b) -{ - struct json_value *ret = json_new_array (); - json_array_append (ret, a); - json_array_append (ret, b); - return ret; -} - -static struct json_value * json_encode_op (char const *op, struct json_value *arg) { struct json_value *ret = json_new_object (); @@ -1300,11 +1291,48 @@ pcond_parse_unary (struct pcond_parser_state *state, struct json_value **ret) } } +static int +is_op (struct json_value *val, char const *opcode) +{ + struct json_value *op; + + if (json_object_get (val, "op", &op) == 0) + { + if (op->type == json_string && strcmp (op->v.s, opcode) == 0) + return 1; + } + return 0; +} + +static void +binop_append_optimized (struct json_value *ar, struct json_value *val, + char const *opcode) +{ + if (is_op (val, opcode)) + { + size_t i, n; + struct json_value *arg; + + json_object_get (val, "arg", &arg); + n = json_array_size (arg); + for (i = 0; i < n; i++) + { + struct json_value *elt; + json_array_get (arg, i, &elt); + json_array_set (arg, i, NULL); + json_array_append (ar, elt); + } + json_value_free (val); + } + else + json_array_append (ar, val); +} + static void pcond_parse_and (struct pcond_parser_state *state, struct json_value **ret) { char const *token; - struct json_value *left, *right; + struct json_value *left, *right, *ar; pcond_parse_unary (state, &left); token = peek_token (state); @@ -1315,14 +1343,17 @@ pcond_parse_and (struct pcond_parser_state *state, struct json_value **ret) } next_token (state); pcond_parse_and (state, &right); - *ret = json_encode_op (token, json_new_array2 (left, right)); + ar = json_new_array (); + json_array_append (ar, left); + binop_append_optimized (ar, right, token); + *ret = json_encode_op (token, ar); } static void pcond_parse_or (struct pcond_parser_state *state, struct json_value **ret) { char const *token; - struct json_value *left, *right; + struct json_value *left, *right, *ar; pcond_parse_and (state, &left); token = peek_token (state); @@ -1333,7 +1364,10 @@ pcond_parse_or (struct pcond_parser_state *state, struct json_value **ret) } next_token (state); pcond_parse_or (state, &right); - *ret = json_encode_op (token, json_new_array2 (left, right)); + ar = json_new_array (); + json_array_append (ar, left); + binop_append_optimized (ar, right, token); + *ret = json_encode_op (token, ar); } static struct json_value * @@ -1369,33 +1403,20 @@ acc_writer (void *closure, char const *text, size_t len) } static char * -json_to_string (struct json_value *val) -{ - struct grecs_txtacc *acc = grecs_txtacc_create (); - struct json_format fmt = { - .indent = 0, - .precision = 0, - .write = acc_writer, - .data = acc - }; - char *ret; - - json_format_value (val, &fmt); - grecs_txtacc_grow_char (acc, 0); - ret = grecs_txtacc_finish (acc, 1); - grecs_txtacc_free (acc); - - return ret; -} - -static char * -parse_condition_to_uri (char const *base, int argc, char **argv) +parse_condition_to_uri (char const *base, int argc, char **argv, int mandatory) { char *ret = NULL; struct grecs_txtacc *acc; struct json_value *val; + if (mandatory && argc == 1) + { + grecs_error (NULL, 0, _("condition must be specified")); + exit (EX_USAGE); + } + acc = grecs_txtacc_create (); + if (base) grecs_txtacc_grow_string (acc, base); val = parse_condition (argc, argv); @@ -1407,6 +1428,7 @@ parse_condition_to_uri (char const *base, int argc, char **argv) .write = acc_writer, .data = acc }; + if (base) grecs_txtacc_grow_char (acc, '/'); json_format_value (val, &fmt); json_value_free (val); @@ -1415,14 +1437,13 @@ parse_condition_to_uri (char const *base, int argc, char **argv) ret = grecs_txtacc_finish (acc, 1); grecs_txtacc_free (acc); - return ret; } static int com_list (struct shttp_connection *conn, int argc, char **argv) { - char *uri = parse_condition_to_uri ("/programs", argc, argv); + char *uri = parse_condition_to_uri ("/programs/select", argc, argv, 0); shttp_io_init (&conn->req); shttp_process (conn, METH_GET, uri); @@ -1443,68 +1464,97 @@ com_list (struct shttp_connection *conn, int argc, char **argv) } static int -com_stop (struct shttp_connection *conn, int argc, char **argv) -{ - char *buf = NULL; - size_t len = 0; - size_t i; - - for (i = 1; i < argc; i++) +json_object_get_type (struct json_value *obj, char const *name, + enum json_value_type type, struct json_value **ret) { struct json_value *v; - grecs_asprintf (&buf, &len, "/programs/%s", argv[i]); - shttp_io_init (&conn->req); - shttp_process (conn, METH_DELETE, buf); - v = shttp_getval (conn, "error_message", json_string); - if (v) - printf ("%s: %s\n", argv[i], v->v.s); + int rc = json_object_get (obj, name, &v); + if (rc) + return rc; + if (v->type != type) + { + errno = EACCES; + return -1; } - free (buf); + *ret = v; return 0; } -static int -com_start (struct shttp_connection *conn, int argc, char **argv) +static void +shttp_print_response_status (struct shttp_connection *conn) { - char *buf = NULL; - size_t len = 0; - size_t i; + if (!dump && conn->result && conn->result->type == json_arr) + { + size_t i, n = json_array_size (conn->result); - for (i = 1; i < argc; i++) + for (i = 0; i < n; i++) { - struct json_value *v; - grecs_asprintf (&buf, &len, "/programs/%s", argv[i]); - shttp_io_init (&conn->req); - shttp_process (conn, METH_PUT, buf); - v = shttp_getval (conn, "error_message", json_string); - if (v) - printf ("%s: %s\n", argv[i], v->v.s); + struct json_value *elt, *v; + + if (json_array_get (conn->result, i, &elt) == 0) + { + if (elt->type != json_object) + { + grecs_error (NULL, 0, _("%lu: unexpected value type"), + (unsigned long) i); + print_json (stderr, v); + continue; + } + if (json_object_get_type (elt, "tag", json_string, &v) == 0) + { + printf ("%s: ", v->v.s); + if (json_object_get_type (elt, "status", json_string, &v) + == 0) + { + if (strcmp (v->v.s, "OK") == 0) + fputs (v->v.s, stdout); + else if (json_object_get_type (elt, "error_message", + json_string, &v) == 0) + fputs (v->v.s, stdout); + else + printf ("unknown error"); + } + else + printf ("unknown status"); + fputc ('\n', stdout); + } + } + } } - free (buf); - return 0; } static int -com_restart (struct shttp_connection *conn, int argc, char **argv) +com_stop (struct shttp_connection *conn, int argc, char **argv) { - struct json_value *val = json_new_array (); - size_t i; + char *uri = parse_condition_to_uri ("/programs/select", argc, argv, 1); - if (argc == 1) + shttp_io_init (&conn->req); + shttp_process (conn, METH_DELETE, uri); + grecs_free (uri); + shttp_print_response_status (conn); + return 0; +} + +static int +com_start (struct shttp_connection *conn, int argc, char **argv) { - grecs_error (NULL, 0, _("missing component names")); - exit (EX_USAGE); + char *uri = parse_condition_to_uri ("/programs/select", argc, argv, 1); + + shttp_io_init (&conn->req); + shttp_process (conn, METH_PUT, uri); + grecs_free (uri); + shttp_print_response_status (conn); + return 0; } - for (i = 1; i < argc; i++) - json_array_append (val, json_new_string (argv[i])); +static int +com_restart (struct shttp_connection *conn, int argc, char **argv) +{ shttp_io_init (&conn->req); - conn->req.content = json_to_string (val); + conn->req.content = parse_condition_to_uri (NULL, argc, argv, 1); conn->req.content_length = strlen (conn->req.content); - json_value_free (val); - - shttp_process (conn, METH_POST, "/programs"); - + shttp_process (conn, METH_POST, "/programs/select"); + shttp_print_response_status (conn); return 0; } @@ -1585,10 +1635,10 @@ struct comtab }; static struct comtab comtab[] = { - { "list", N_("[EXPR]"), N_("list configured components"), com_list }, - { "stop", N_("TAG [TAG...]"), N_("stop components"), com_stop }, - { "start", N_("TAG [TAG...]"), N_("start components"), com_start }, - { "restart", N_("TAG [TAG...]"), N_("restart components"), com_restart }, + { "list", N_("[CONDITION]"), N_("list configured components"), com_list }, + { "stop", N_("CONDITION"), N_("stop components"), com_stop }, + { "start", N_("CONDITION"), N_("start components"), com_start }, + { "restart", N_("CONDITION"), N_("restart components"), com_restart }, { "id", N_("[KEYWORDS...]"), N_("show info about the running GNU Pies instance"), com_id }, @@ -1639,7 +1689,7 @@ command_help (FILE *fp) { struct comtab *cp; - fputs (_("Available commands are:"), fp); + fputs (_("Available commands with their arguments are:"), fp); fputc ('\n', fp); fputc ('\n', fp); @@ -1655,6 +1705,24 @@ command_help (FILE *fp) fprintf (fp, " %s\n", gettext (cp->docstr)); } fputc ('\n', fp); + fputs (_("Condition is defined as:"), fp); + fputc ('\n', fp); + fputc ('\n', fp); + fputs ("\ + <condition> ::= <disjunction>\n\ + <disjunction> ::= <conjunction> | <conjunction> \"or\" <disjunction>\n\ + <conjunction> ::= <unary> | <unary> \"and\" <conjunction>\n\ + <unary> ::= <term> | \"not\" <condition> | \"(\" <condition> \")\"\n\ + <term> ::= \"all\" | <keyword> <value>\n\ + <keyword> ::= \"type\" | \"mode\" | \"status\" | \"component\"\n\ + <value> ::= <word> | <quoted-string>\n\ + <word> ::= <printable> | <word> <printable>\n\ + <printable> ::= \"A\" - \"Z\" | \"a\" - \"z\" | \"0\" - \"9\" |\n\ + \"_\" | \".\" | \"*\" | \":\" | \"@\" | \"[\" | \"]\" | \"-\" | \"/\"\n\ + <quoted-string> ::= \"\"\" <string> \"\"\"\n\ + <string> ::= <char> | <string> <char>\n\ + <char> ::= <any character except \"\\\" and \"\"\"> | \"\\\\\" | \"\\\"\"\n\n", fp); + } int |