blob: 8773907533947c99a924d6636cfe31e6460d1402 [file] [log] [blame]
Radek Krejci5819f7c2019-05-31 14:53:29 +02001/**
2 * @file main.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief libyang's YANG Regular Expression tool
5 *
6 * Copyright (c) 2017 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14
Radek Krejci535ea9f2020-05-29 16:01:05 +020015#define _GNU_SOURCE
Radek Krejci5819f7c2019-05-31 14:53:29 +020016
17#include <errno.h>
18#include <fcntl.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <sys/stat.h>
22#include <sys/types.h>
23#include <string.h>
24#include <getopt.h>
25#include <unistd.h>
26
Radek Krejci535ea9f2020-05-29 16:01:05 +020027#include "libyang.h"
28
29#include "tools/config.h"
Radek Krejci5819f7c2019-05-31 14:53:29 +020030
31void
32help(void)
33{
34 fprintf(stdout, "YANG Regular Expressions processor.\n");
35 fprintf(stdout, "Usage:\n");
36 fprintf(stdout, " yangre [-hv]\n");
37 fprintf(stdout, " yangre [-V] -p <regexp1> [-i] [-p <regexp2> [-i] ...] <string>\n");
38 fprintf(stdout, " yangre [-V] -f <file>\n");
39 fprintf(stdout, "Returns 0 if string matches the pattern(s), 1 if not and -1 on error.\n\n");
40 fprintf(stdout, "Options:\n"
41 " -h, --help Show this help message and exit.\n"
42 " -v, --version Show version number and exit.\n"
43 " -V, --verbose Print the processing information.\n"
44 " -i, --invert-match Invert-match modifier for the closest preceding\n"
45 " pattern.\n"
46 " -p, --pattern=\"REGEXP\" Regular expression including the quoting,\n"
47 " which is applied the same way as in a YANG module.\n"
48 " -f, --file=\"FILE\" List of patterns and the <string> (separated by an\n"
49 " empty line) are taken from <file>. Invert-match is\n"
50 " indicated by the single space character at the \n"
51 " beginning of the pattern line. YANG quotation around\n"
52 " patterns is still expected, but that avoids issues with\n"
53 " reading quotation by shell. Avoid newline at the end\n"
54 " of the string line to represent empty <string>.");
55 fprintf(stdout, "Examples:\n"
56 " pattern \"[0-9a-fA-F]*\"; -> yangre -p '\"[0-9a-fA-F]*\"' '1F'\n"
57 " pattern '[a-zA-Z0-9\\-_.]*'; -> yangre -p \"'[a-zA-Z0-9\\-_.]*'\" 'a-b'\n"
58 " pattern [xX][mM][lL].*; -> yangre -p '[xX][mM][lL].*' 'xml-encoding'\n\n");
59 fprintf(stdout, "Note that to pass YANG quoting through your shell, you are supposed to use\n"
60 "the other quotation around. For not-quoted patterns, use single quotes.\n\n");
61}
62
63void
64version(void)
65{
66 fprintf(stdout, "yangre %s\n", PROJECT_VERSION);
67}
68
69void
70pattern_error(LY_LOG_LEVEL level, const char *msg, const char *path)
71{
72 (void) path; /* unused */
73
74 if (level == LY_LLERR && strcmp(msg, "Module \"yangre\" parsing failed.")) {
75 fprintf(stderr, "yangre error: %s\n", msg);
76 }
77}
78
79static const char *module_start = "module yangre {"
80 "yang-version 1.1;"
81 "namespace urn:cesnet:libyang:yangre;"
82 "prefix re;"
83 "leaf pattern {"
84 " type string {";
85static const char *module_invertmatch = " { modifier invert-match; }";
86static const char *module_match = ";";
87static const char *module_end = "}}}";
88
89static int
90add_pattern(char ***patterns, int **inverts, int *counter, char *pattern)
91{
92 void *reallocated1, *reallocated2;
93
94 (*counter)++;
95 reallocated1 = realloc(*patterns, *counter * sizeof **patterns);
96 reallocated2 = realloc(*inverts, *counter * sizeof **inverts);
97 if (!reallocated1 || !reallocated2) {
98 fprintf(stderr, "yangre error: memory allocation error.\n");
99 free(reallocated1);
100 free(reallocated2);
101 return EXIT_FAILURE;
102 }
103 (*patterns) = reallocated1;
104 (*patterns)[*counter - 1] = strdup(pattern);
105 (*inverts) = reallocated2;
106 (*inverts)[*counter - 1] = 0;
107
108 return EXIT_SUCCESS;
109}
110
111int
112main(int argc, char* argv[])
113{
114 LY_ERR match;
115 int i, opt_index = 0, ret = -1, verbose = 0, blankline = 0;
116 struct option options[] = {
117 {"help", no_argument, NULL, 'h'},
118 {"file", required_argument, NULL, 'f'},
119 {"invert-match", no_argument, NULL, 'i'},
120 {"pattern", required_argument, NULL, 'p'},
121 {"version", no_argument, NULL, 'v'},
122 {"verbose", no_argument, NULL, 'V'},
123 {NULL, 0, NULL, 0}
124 };
125 char **patterns = NULL, *str = NULL, *modstr = NULL, *s;
126 int *invert_match = NULL;
127 int patterns_count = 0;
128 struct ly_ctx *ctx = NULL;
129 const struct lys_module *mod;
130 FILE *infile = NULL;
131 size_t len = 0;
132 ssize_t l;
133 struct lysc_type *type;
Radek Krejci99afd3a2019-06-03 09:30:57 +0200134 struct ly_err_item *err = NULL;
Radek Krejci5819f7c2019-05-31 14:53:29 +0200135
136 opterr = 0;
137 while ((i = getopt_long(argc, argv, "hf:ivVp:", options, &opt_index)) != -1) {
138 switch (i) {
139 case 'h':
140 help();
141 ret = -2; /* continue to allow printing version and help at once */
142 break;
143 case 'f':
144 if (infile) {
145 help();
146 fprintf(stderr, "yangre error: multiple input files are not supported.\n");
147 goto cleanup;
148 } else if (patterns_count) {
149 help();
150 fprintf(stderr, "yangre error: command line patterns cannot be mixed with file input.\n");
151 goto cleanup;
152 }
153 infile = fopen(optarg, "r");
154 if (!infile) {
155 fprintf(stderr, "yangre error: unable to open input file %s (%s).\n", optarg, strerror(errno));
156 goto cleanup;
157 }
158
159 while((l = getline(&str, &len, infile)) != -1) {
160 if (!blankline && str[0] == '\n') {
161 /* blank line */
162 blankline = 1;
163 continue;
164 }
165 if (str[0] != '\n' && str[l - 1] == '\n') {
166 /* remove ending newline */
167 str[l - 1] = '\0';
168 }
169 if (blankline) {
170 /* done - str is now the string to check */
171 blankline = 0;
172 break;
173 /* else read the patterns */
174 } else if (add_pattern(&patterns, &invert_match, &patterns_count,
175 str[0] == ' ' ? &str[1] : str)) {
176 goto cleanup;
177 }
178 if (str[0] == ' ') {
179 /* set invert-match */
180 invert_match[patterns_count - 1] = 1;
181 }
182 }
183 if (blankline) {
184 /* corner case, no input after blankline meaning the pattern to check is empty */
185 if (str != NULL) {
186 free(str);
187 }
188 str = malloc(sizeof(char));
189 str[0] = '\0';
190 }
191 break;
192 case 'i':
193 if (!patterns_count || invert_match[patterns_count - 1]) {
194 help();
195 fprintf(stderr, "yangre error: invert-match option must follow some pattern.\n");
196 goto cleanup;
197 }
198 invert_match[patterns_count - 1] = 1;
199 break;
200 case 'p':
201 if (infile) {
202 help();
203 fprintf(stderr, "yangre error: command line patterns cannot be mixed with file input.\n");
204 goto cleanup;
205 }
206 if (add_pattern(&patterns, &invert_match, &patterns_count, optarg)) {
207 goto cleanup;
208 }
209 break;
210 case 'v':
211 version();
212 ret = -2; /* continue to allow printing version and help at once */
213 break;
214 case 'V':
215 verbose = 1;
216 break;
217 default:
218 help();
219 if (optopt) {
220 fprintf(stderr, "yangre error: invalid option: -%c\n", optopt);
221 } else {
222 fprintf(stderr, "yangre error: invalid option: %s\n", argv[optind - 1]);
223 }
224 goto cleanup;
225 }
226 }
227
228 if (ret == -2) {
229 goto cleanup;
230 }
231
232 if (!str) {
233 /* check options compatibility */
234 if (optind >= argc) {
235 help();
236 fprintf(stderr, "yangre error: missing <string> parameter to process.\n");
237 goto cleanup;
238 } else if (!patterns_count) {
239 help();
240 fprintf(stderr, "yangre error: missing pattern parameter to use.\n");
241 goto cleanup;
242 }
243 str = argv[optind];
244 }
245
246 for (modstr = (char*)module_start, i = 0; i < patterns_count; i++) {
247 if (asprintf(&s, "%s pattern %s%s", modstr, patterns[i], invert_match[i] ? module_invertmatch : module_match) == -1) {
248 fprintf(stderr, "yangre error: memory allocation failed.\n");
249 goto cleanup;
250 }
251 if (modstr != module_start) {
252 free(modstr);
253 }
254 modstr = s;
255 }
256 if (asprintf(&s, "%s%s", modstr, module_end) == -1) {
257 fprintf(stderr, "yangre error: memory allocation failed.\n");
258 goto cleanup;
259 }
260 if (modstr != module_start) {
261 free(modstr);
262 }
263 modstr = s;
264
265 if (ly_ctx_new(NULL, 0, &ctx)) {
266 goto cleanup;
267 }
268
269 ly_set_log_clb(pattern_error, 0);
270 mod = lys_parse_mem(ctx, modstr, LYS_IN_YANG);
271 if (!mod || !mod->compiled || !mod->compiled->data) {
272 goto cleanup;
273 }
274
275 type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
Radek Krejci73dead22019-07-11 16:46:16 +0200276 match = type->plugin->store(ctx, type, str, strlen(str), 0, NULL, NULL, LYD_XML, NULL, NULL, NULL, NULL, &err);
Radek Krejci5819f7c2019-05-31 14:53:29 +0200277 if (verbose) {
278 for (i = 0; i < patterns_count; i++) {
279 fprintf(stdout, "pattern %d: %s\n", i + 1, patterns[i]);
280 fprintf(stdout, "matching %d: %s\n", i + 1, invert_match[i] ? "inverted" : "regular");
281 }
282 fprintf(stdout, "string : %s\n", str);
283 if (match == LY_SUCCESS) {
284 fprintf(stdout, "result : matching\n");
285 } else if (match == LY_EVALID) {
286 fprintf(stdout, "result : not matching\n");
287 } else {
288 fprintf(stdout, "result : error (%s)\n", err->msg);
289 }
290 }
291 if (match == LY_SUCCESS) {
292 ret = 0;
293 } else if (match == LY_EVALID) {
294 ret = 1;
295 } else {
296 ret = -1;
297 }
298
299cleanup:
300 ly_ctx_destroy(ctx, NULL);
301 for (i = 0; i < patterns_count; i++) {
302 free(patterns[i]);
303 }
304 free(patterns);
305 free(invert_match);
306 free(modstr);
307 if (infile) {
308 fclose(infile);
309 free(str);
310 }
311 ly_err_free(err);
312
313 return ret;
314}