File src/engine/parsing.c changed (mode: 100644) (index 746475e39..f19e2e394) |
48 |
48 |
#include <ctype.h> /* isalnum() isalpha() tolower() */ |
#include <ctype.h> /* isalnum() isalpha() tolower() */ |
49 |
49 |
#include <stddef.h> /* NULL size_t */ |
#include <stddef.h> /* NULL size_t */ |
50 |
50 |
#include <stdio.h> /* snprintf() */ |
#include <stdio.h> /* snprintf() */ |
51 |
|
#include <stdlib.h> |
|
|
51 |
|
#include <stdlib.h> /* free() */ |
52 |
52 |
#include <string.h> /* strcat() strcmp() strncpy() */ |
#include <string.h> /* strcat() strcmp() strncpy() */ |
|
53 |
|
#include <wchar.h> /* wchar_t */ |
53 |
54 |
|
|
54 |
55 |
#include "../compat/reallocarray.h" |
#include "../compat/reallocarray.h" |
55 |
56 |
#include "../utils/str.h" |
#include "../utils/str.h" |
|
59 |
60 |
#include "var.h" |
#include "var.h" |
60 |
61 |
#include "variables.h" |
#include "variables.h" |
61 |
62 |
|
|
|
63 |
|
#define NOTATION_LENGTH_MAX 16 |
62 |
64 |
#define VAR_NAME_LENGTH_MAX 1024 |
#define VAR_NAME_LENGTH_MAX 1024 |
63 |
65 |
#define CMD_LINE_LENGTH_MAX 4096 |
#define CMD_LINE_LENGTH_MAX 4096 |
64 |
66 |
|
|
|
... |
... |
static int parse_singly_quoted_char(parse_context_t *ctx, const char **in, |
177 |
179 |
static var_t parse_doubly_quoted_string(parse_context_t *ctx, const char **in); |
static var_t parse_doubly_quoted_string(parse_context_t *ctx, const char **in); |
178 |
180 |
static int parse_doubly_quoted_char(parse_context_t *ctx, const char **in, |
static int parse_doubly_quoted_char(parse_context_t *ctx, const char **in, |
179 |
181 |
sbuffer *sbuf); |
sbuffer *sbuf); |
|
182 |
|
static int parse_doubly_quoted_notation(parse_context_t *ctx, const char **in, |
|
183 |
|
sbuffer *sbuf); |
180 |
184 |
static var_t eval_envvar(parse_context_t *ctx, const char **in); |
static var_t eval_envvar(parse_context_t *ctx, const char **in); |
181 |
185 |
static var_t eval_var(parse_context_t *ctx, const char **in); |
static var_t eval_var(parse_context_t *ctx, const char **in); |
182 |
186 |
static var_t eval_opt(parse_context_t *ctx, const char **in); |
static var_t eval_opt(parse_context_t *ctx, const char **in); |
|
... |
... |
static void get_next(parse_context_t *ctx, const char **in); |
191 |
195 |
|
|
192 |
196 |
static int initialized; |
static int initialized; |
193 |
197 |
static getenv_func getenv_fu; |
static getenv_func getenv_fu; |
|
198 |
|
static notation_func notation_fu; /* Can be NULL. */ |
194 |
199 |
|
|
195 |
200 |
/* Empty expression to be returned on errors. */ |
/* Empty expression to be returned on errors. */ |
196 |
201 |
static expr_t null_expr; |
static expr_t null_expr; |
|
... |
... |
void |
201 |
206 |
vle_parser_init(getenv_func getenv_f) |
vle_parser_init(getenv_func getenv_f) |
202 |
207 |
{ |
{ |
203 |
208 |
getenv_fu = getenv_f; |
getenv_fu = getenv_f; |
|
209 |
|
notation_fu = NULL; |
204 |
210 |
initialized = 1; |
initialized = 1; |
205 |
211 |
} |
} |
206 |
212 |
|
|
|
213 |
|
void |
|
214 |
|
vle_parser_set_notation(notation_func notation_f) |
|
215 |
|
{ |
|
216 |
|
assert(initialized && "Parser must be initialized before configuration."); |
|
217 |
|
notation_fu = notation_f; |
|
218 |
|
} |
|
219 |
|
|
207 |
220 |
parsing_result_t |
parsing_result_t |
208 |
221 |
vle_parser_eval(const char input[], int interactive) |
vle_parser_eval(const char input[], int interactive) |
209 |
222 |
{ |
{ |
|
... |
... |
parse_doubly_quoted_string(parse_context_t *ctx, const char **in) |
1060 |
1073 |
return var_false(); |
return var_false(); |
1061 |
1074 |
} |
} |
1062 |
1075 |
|
|
1063 |
|
/* dqchar |
|
|
1076 |
|
/* dqchar ::= [^"\] | '\' dqesc |
|
1077 |
|
* dqesc ::= '\\' | '\0' | '\b' | '\e' | '\n' | '\r' | '\t' | dqbn |
1064 |
1078 |
* Returns non-zero if there are more characters in the string. */ |
* Returns non-zero if there are more characters in the string. */ |
1065 |
1079 |
int |
int |
1066 |
1080 |
parse_doubly_quoted_char(parse_context_t *ctx, const char **in, sbuffer *sbuf) |
parse_doubly_quoted_char(parse_context_t *ctx, const char **in, sbuffer *sbuf) |
|
... |
... |
parse_doubly_quoted_char(parse_context_t *ctx, const char **in, sbuffer *sbuf) |
1107 |
1121 |
ctx->last_error = PE_INVALID_EXPRESSION; |
ctx->last_error = PE_INVALID_EXPRESSION; |
1108 |
1122 |
return 0; |
return 0; |
1109 |
1123 |
} |
} |
|
1124 |
|
|
|
1125 |
|
if(ctx->last_token.c == '<') |
|
1126 |
|
{ |
|
1127 |
|
parse_context_t tmp_ctx = *ctx; |
|
1128 |
|
const char *tmp_in = *in; |
|
1129 |
|
if(parse_doubly_quoted_notation(&tmp_ctx, &tmp_in, sbuf) == 0) |
|
1130 |
|
{ |
|
1131 |
|
*ctx = tmp_ctx; |
|
1132 |
|
*in = tmp_in; |
|
1133 |
|
return 1; |
|
1134 |
|
} |
|
1135 |
|
|
|
1136 |
|
if(tmp_ctx.last_error != PE_NO_ERROR) |
|
1137 |
|
{ |
|
1138 |
|
*ctx = tmp_ctx; |
|
1139 |
|
return 0; |
|
1140 |
|
} |
|
1141 |
|
} |
|
1142 |
|
|
1110 |
1143 |
ok = sstrappendch(sbuf->data, &sbuf->len, sbuf->size, |
ok = sstrappendch(sbuf->data, &sbuf->len, sbuf->size, |
1111 |
1144 |
table[(unsigned char)ctx->last_token.c]); |
table[(unsigned char)ctx->last_token.c]); |
1112 |
1145 |
} |
} |
|
... |
... |
parse_doubly_quoted_char(parse_context_t *ctx, const char **in, sbuffer *sbuf) |
1125 |
1158 |
return 1; |
return 1; |
1126 |
1159 |
} |
} |
1127 |
1160 |
|
|
|
1161 |
|
/* dqbn ::= '<' [^>]* '>' |
|
1162 |
|
* Parses bracket notation (like <cr>). Returns zero on success. */ |
|
1163 |
|
static int |
|
1164 |
|
parse_doubly_quoted_notation(parse_context_t *ctx, const char **in, |
|
1165 |
|
sbuffer *sbuf) |
|
1166 |
|
{ |
|
1167 |
|
if(notation_fu == NULL) |
|
1168 |
|
{ |
|
1169 |
|
return 1; |
|
1170 |
|
} |
|
1171 |
|
|
|
1172 |
|
char notation[NOTATION_LENGTH_MAX + 1]; |
|
1173 |
|
notation[0] = '\0'; |
|
1174 |
|
size_t len = 0; |
|
1175 |
|
while(ctx->last_token.type != END && ctx->last_token.c != '>' && |
|
1176 |
|
len < sizeof(notation) - 1) |
|
1177 |
|
{ |
|
1178 |
|
(void)sstrappendch(notation, &len, sizeof(notation), ctx->last_token.c); |
|
1179 |
|
get_next(ctx, in); |
|
1180 |
|
} |
|
1181 |
|
|
|
1182 |
|
if(ctx->last_token.c != '>') |
|
1183 |
|
{ |
|
1184 |
|
return 1; |
|
1185 |
|
} |
|
1186 |
|
|
|
1187 |
|
(void)sstrappendch(notation, &len, sizeof(notation), '>'); |
|
1188 |
|
get_next(ctx, in); |
|
1189 |
|
|
|
1190 |
|
const wchar_t *expanded = notation_fu(notation); |
|
1191 |
|
if(expanded == NULL) |
|
1192 |
|
{ |
|
1193 |
|
return 1; |
|
1194 |
|
} |
|
1195 |
|
|
|
1196 |
|
char *expanded_mb = to_multibyte(expanded); |
|
1197 |
|
if(expanded_mb == NULL) |
|
1198 |
|
{ |
|
1199 |
|
ctx->last_error = PE_INTERNAL; |
|
1200 |
|
return 1; |
|
1201 |
|
} |
|
1202 |
|
|
|
1203 |
|
int err = sstrappend(sbuf->data, &sbuf->len, sbuf->size, expanded_mb); |
|
1204 |
|
free(expanded_mb); |
|
1205 |
|
|
|
1206 |
|
if(err != 0) |
|
1207 |
|
{ |
|
1208 |
|
ctx->last_error = PE_INTERNAL; |
|
1209 |
|
return 1; |
|
1210 |
|
} |
|
1211 |
|
|
|
1212 |
|
return 0; |
|
1213 |
|
} |
|
1214 |
|
|
1128 |
1215 |
/* envvar ::= '$' envvarname */ |
/* envvar ::= '$' envvarname */ |
1129 |
1216 |
static var_t |
static var_t |
1130 |
1217 |
eval_envvar(parse_context_t *ctx, const char **in) |
eval_envvar(parse_context_t *ctx, const char **in) |
File src/engine/parsing.h changed (mode: 100644) (index 360c19db5..badcbc3a9) |
19 |
19 |
#ifndef VIFM__ENGINE__PARSING_H__ |
#ifndef VIFM__ENGINE__PARSING_H__ |
20 |
20 |
#define VIFM__ENGINE__PARSING_H__ |
#define VIFM__ENGINE__PARSING_H__ |
21 |
21 |
|
|
|
22 |
|
#include <wchar.h> /* wchar_t */ |
|
23 |
|
|
22 |
24 |
#include "var.h" |
#include "var.h" |
23 |
25 |
|
|
24 |
26 |
/* An enumeration of possible parsing errors. */ |
/* An enumeration of possible parsing errors. */ |
|
... |
... |
parsing_result_t; |
55 |
57 |
* string. The function should not allocate new string. */ |
* string. The function should not allocate new string. */ |
56 |
58 |
typedef const char * (*getenv_func)(const char *envname); |
typedef const char * (*getenv_func)(const char *envname); |
57 |
59 |
|
|
|
60 |
|
/* Type of a function that will be used to resolve bracket notation (e.g., |
|
61 |
|
* <cr>) to its value. If the notation doesn't exist, the function must return |
|
62 |
|
* NULL. The function must not allocate a new string. */ |
|
63 |
|
typedef const wchar_t * (*notation_func)(const char str[]); |
|
64 |
|
|
58 |
65 |
/* A type of function that will be used to print error messages. */ |
/* A type of function that will be used to print error messages. */ |
59 |
66 |
typedef void (*print_error_func)(const char msg[]); |
typedef void (*print_error_func)(const char msg[]); |
60 |
67 |
|
|
61 |
68 |
/* Can be called several times. getenv_f can be NULL. */ |
/* Can be called several times. getenv_f can be NULL. */ |
62 |
69 |
void vle_parser_init(getenv_func getenv_f); |
void vle_parser_init(getenv_func getenv_f); |
63 |
70 |
|
|
|
71 |
|
/* Sets optional function which resolves bracket notation (like <cr>). The |
|
72 |
|
* parameter can be NULL. */ |
|
73 |
|
void vle_parser_set_notation(notation_func notation_f); |
|
74 |
|
|
64 |
75 |
/* Performs parsing and evaluation. Returns structure describing the outcome. |
/* Performs parsing and evaluation. Returns structure describing the outcome. |
65 |
76 |
* Field value of the result should be freed by the caller. */ |
* Field value of the result should be freed by the caller. */ |
66 |
77 |
parsing_result_t vle_parser_eval(const char input[], int interactive); |
parsing_result_t vle_parser_eval(const char input[], int interactive); |
File tests/parsing/double_quotes.c changed (mode: 100644) (index 84b693d63..beaeca832) |
1 |
1 |
#include <stic.h> |
#include <stic.h> |
2 |
2 |
|
|
3 |
3 |
#include <stdlib.h> /* free() */ |
#include <stdlib.h> /* free() */ |
4 |
|
#include <string.h> /* memset() */ |
|
|
4 |
|
#include <string.h> /* memset() strcmp() */ |
|
5 |
|
#include <wchar.h> /* wchar_t */ |
5 |
6 |
|
|
6 |
7 |
#include "../../src/engine/parsing.h" |
#include "../../src/engine/parsing.h" |
7 |
8 |
#include "../../src/engine/var.h" |
#include "../../src/engine/var.h" |
|
9 |
|
#include "../../src/utils/macros.h" |
8 |
10 |
|
|
9 |
11 |
#include "asserts.h" |
#include "asserts.h" |
10 |
12 |
|
|
|
13 |
|
static const wchar_t * expand_notation(const char str[]); |
|
14 |
|
|
11 |
15 |
TEST(empty_ok) |
TEST(empty_ok) |
12 |
16 |
{ |
{ |
13 |
17 |
ASSERT_OK("\"\"", ""); |
ASSERT_OK("\"\"", ""); |
|
... |
... |
TEST(very_long_string) |
66 |
70 |
ASSERT_FAIL(string, PE_INTERNAL); |
ASSERT_FAIL(string, PE_INTERNAL); |
67 |
71 |
} |
} |
68 |
72 |
|
|
|
73 |
|
TEST(bracket_notation) |
|
74 |
|
{ |
|
75 |
|
/* No notation by default. */ |
|
76 |
|
vle_parser_set_notation(NULL); |
|
77 |
|
ASSERT_OK("\"\\<space>\"", "<space>"); |
|
78 |
|
|
|
79 |
|
vle_parser_set_notation(&expand_notation); |
|
80 |
|
|
|
81 |
|
/* Unescaped notation. */ |
|
82 |
|
ASSERT_OK("\"<space>\"", "<space>"); |
|
83 |
|
|
|
84 |
|
/* Escaped notation. */ |
|
85 |
|
ASSERT_OK("\"\\<space>\"", " "); |
|
86 |
|
|
|
87 |
|
/* Unrecognized notation. */ |
|
88 |
|
ASSERT_OK("\"\\<notspace>\"", "<notspace>"); |
|
89 |
|
|
|
90 |
|
/* Unrecognized notation which is too long for the parser. */ |
|
91 |
|
ASSERT_OK("\"\\<nosuchexcessivelylongnotation>\"", |
|
92 |
|
"<nosuchexcessivelylongnotation>"); |
|
93 |
|
|
|
94 |
|
/* Recognized notation which is too long for the parser. */ |
|
95 |
|
ASSERT_OK("\"\\<thisnotationiswaytoolong>\"", "<thisnotationiswaytoolong>"); |
|
96 |
|
|
|
97 |
|
/* Unfinished notation. */ |
|
98 |
|
ASSERT_OK("\"\\<space\"", "<space"); |
|
99 |
|
|
|
100 |
|
/* Nested notation. */ |
|
101 |
|
ASSERT_OK("\"\\<sp\\ace>\"", "<space>"); |
|
102 |
|
ASSERT_OK("\"\\<sp\\<a>ce>\"", "<sp<a>ce>"); |
|
103 |
|
ASSERT_OK("\"\\<\\<tab>>\"", "<\t>"); |
|
104 |
|
|
|
105 |
|
/* Unrealistically long expansion. */ |
|
106 |
|
ASSERT_FAIL("\"\\<very_long>\"", PE_INTERNAL); |
|
107 |
|
|
|
108 |
|
/* More realistic case. */ |
|
109 |
|
ASSERT_OK("\"ma\\<space>yy\\<tab>\\<tab>p'a\"", "ma yy\t\tp'a"); |
|
110 |
|
} |
|
111 |
|
|
|
112 |
|
static const wchar_t * |
|
113 |
|
expand_notation(const char str[]) |
|
114 |
|
{ |
|
115 |
|
static wchar_t very_long[5*1024]; |
|
116 |
|
|
|
117 |
|
if(very_long[0] == '\0') |
|
118 |
|
{ |
|
119 |
|
int i; |
|
120 |
|
for(i = 0; i < (int)ARRAY_LEN(very_long) - 1; ++i) |
|
121 |
|
{ |
|
122 |
|
very_long[i] = L'1'; |
|
123 |
|
} |
|
124 |
|
} |
|
125 |
|
|
|
126 |
|
if(strcmp(str, "<thisnotationiswaytoolong>") == 0) |
|
127 |
|
{ |
|
128 |
|
return L"!!!"; |
|
129 |
|
} |
|
130 |
|
if(strcmp(str, "<very_long>") == 0) |
|
131 |
|
{ |
|
132 |
|
return very_long; |
|
133 |
|
} |
|
134 |
|
if(strcmp(str, "<space>") == 0) |
|
135 |
|
{ |
|
136 |
|
return L" "; |
|
137 |
|
} |
|
138 |
|
if(strcmp(str, "<tab>") == 0) |
|
139 |
|
{ |
|
140 |
|
return L"\t"; |
|
141 |
|
} |
|
142 |
|
return NULL; |
|
143 |
|
} |
|
144 |
|
|
69 |
145 |
/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ |
/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ |
70 |
146 |
/* vim: set cinoptions+=t0 filetype=c : */ |
/* vim: set cinoptions+=t0 filetype=c : */ |