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