blob: 6e15d9ee92a13e64440371a63aacc39157cdd758 [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
Christian Hopps32874e12021-05-01 09:43:54 -040015#define _GNU_SOURCE /* asprintf, strdup */
Radek Krejci5819f7c2019-05-31 14:53:29 +020016
17#include <errno.h>
Radek Krejci4001a102020-11-13 15:43:05 +010018#include <getopt.h>
Radek Krejci5819f7c2019-05-31 14:53:29 +020019#include <stdio.h>
20#include <stdlib.h>
Radek Krejci5819f7c2019-05-31 14:53:29 +020021#include <string.h>
Radek Krejci4001a102020-11-13 15:43:05 +010022#include <sys/types.h>
Radek Krejci5819f7c2019-05-31 14:53:29 +020023
Radek Krejci535ea9f2020-05-29 16:01:05 +020024#include "libyang.h"
25
Michal Vasko5aa44c02020-06-29 11:47:02 +020026#include "compat.h"
Radek Krejci4001a102020-11-13 15:43:05 +010027#include "tools/config.h"
Radek Krejci5819f7c2019-05-31 14:53:29 +020028
29void
30help(void)
31{
32 fprintf(stdout, "YANG Regular Expressions processor.\n");
33 fprintf(stdout, "Usage:\n");
34 fprintf(stdout, " yangre [-hv]\n");
35 fprintf(stdout, " yangre [-V] -p <regexp1> [-i] [-p <regexp2> [-i] ...] <string>\n");
36 fprintf(stdout, " yangre [-V] -f <file>\n");
aPiecekbfdf83b2023-06-29 13:55:09 +020037 fprintf(stdout, "Returns 0 if string matches the pattern(s) or if otherwise successful.\n");
38 fprintf(stdout, "Returns 1 on error.\n");
39 fprintf(stdout, "Returns 2 if string does not match the pattern(s).\n\n");
Radek Krejci5819f7c2019-05-31 14:53:29 +020040 fprintf(stdout, "Options:\n"
Radek Krejci4001a102020-11-13 15:43:05 +010041 " -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>.");
Radek Krejci5819f7c2019-05-31 14:53:29 +020055 fprintf(stdout, "Examples:\n"
Radek Krejci4001a102020-11-13 15:43:05 +010056 " 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");
Radek Krejci5819f7c2019-05-31 14:53:29 +020059 fprintf(stdout, "Note that to pass YANG quoting through your shell, you are supposed to use\n"
Radek Krejci4001a102020-11-13 15:43:05 +010060 "the other quotation around. For not-quoted patterns, use single quotes.\n\n");
Radek Krejci5819f7c2019-05-31 14:53:29 +020061}
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
Radek Krejcid7857262020-11-06 10:29:22 +010074 if (level == LY_LLERR) {
Radek Krejci5819f7c2019-05-31 14:53:29 +020075 fprintf(stderr, "yangre error: %s\n", msg);
76 }
77}
78
79static const char *module_start = "module yangre {"
Radek Krejci4001a102020-11-13 15:43:05 +010080 "yang-version 1.1;"
81 "namespace urn:cesnet:libyang:yangre;"
82 "prefix re;"
83 "leaf pattern {"
84 " type string {";
Radek Krejci5819f7c2019-05-31 14:53:29 +020085static 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;
aPiecekb2b51362023-06-22 09:14:59 +020093 int orig_counter;
Radek Krejci5819f7c2019-05-31 14:53:29 +020094
aPiecekb2b51362023-06-22 09:14:59 +020095 /* Store the original number of items. */
96 orig_counter = *counter;
97
98 /* Reallocate 'patterns' memory with additional space. */
99 reallocated1 = realloc(*patterns, (orig_counter + 1) * sizeof **patterns);
100 if (!reallocated1) {
101 goto error;
Radek Krejci5819f7c2019-05-31 14:53:29 +0200102 }
103 (*patterns) = reallocated1;
aPiecekb2b51362023-06-22 09:14:59 +0200104 /* Allocated memory is now larger. */
105 (*counter)++;
106 /* Copy the pattern and store it to the additonal space. */
107 (*patterns)[orig_counter] = strdup(pattern);
108 if (!(*patterns)[orig_counter]) {
109 goto error;
110 }
111
112 /* Reallocate 'inverts' memory with additional space. */
113 reallocated2 = realloc(*inverts, (orig_counter + 1) * sizeof **inverts);
114 if (!reallocated2) {
115 goto error;
116 }
Radek Krejci5819f7c2019-05-31 14:53:29 +0200117 (*inverts) = reallocated2;
aPiecekb2b51362023-06-22 09:14:59 +0200118 (*inverts)[orig_counter] = 0;
Radek Krejci5819f7c2019-05-31 14:53:29 +0200119
aPiecekbfdf83b2023-06-29 13:55:09 +0200120 return 0;
aPiecekb2b51362023-06-22 09:14:59 +0200121
122error:
123 fprintf(stderr, "yangre error: memory allocation error.\n");
aPiecekbfdf83b2023-06-29 13:55:09 +0200124 return 1;
Radek Krejci5819f7c2019-05-31 14:53:29 +0200125}
126
127int
Radek Krejci4001a102020-11-13 15:43:05 +0100128main(int argc, char *argv[])
Radek Krejci5819f7c2019-05-31 14:53:29 +0200129{
130 LY_ERR match;
aPiecekbfdf83b2023-06-29 13:55:09 +0200131 int i, opt_index = 0, ret = 1, verbose = 0, blankline = 0;
Radek Krejci5819f7c2019-05-31 14:53:29 +0200132 struct option options[] = {
133 {"help", no_argument, NULL, 'h'},
134 {"file", required_argument, NULL, 'f'},
135 {"invert-match", no_argument, NULL, 'i'},
136 {"pattern", required_argument, NULL, 'p'},
137 {"version", no_argument, NULL, 'v'},
138 {"verbose", no_argument, NULL, 'V'},
139 {NULL, 0, NULL, 0}
140 };
141 char **patterns = NULL, *str = NULL, *modstr = NULL, *s;
142 int *invert_match = NULL;
143 int patterns_count = 0;
144 struct ly_ctx *ctx = NULL;
Michal Vasko4de7d072021-07-09 09:13:18 +0200145 struct lys_module *mod;
Radek Krejci5819f7c2019-05-31 14:53:29 +0200146 FILE *infile = NULL;
147 size_t len = 0;
148 ssize_t l;
aPiecekbfdf83b2023-06-29 13:55:09 +0200149 ly_bool info_printed = 0;
Radek Krejci5819f7c2019-05-31 14:53:29 +0200150
151 opterr = 0;
152 while ((i = getopt_long(argc, argv, "hf:ivVp:", options, &opt_index)) != -1) {
153 switch (i) {
154 case 'h':
155 help();
aPiecekbfdf83b2023-06-29 13:55:09 +0200156 info_printed = 1;
Radek Krejci5819f7c2019-05-31 14:53:29 +0200157 break;
158 case 'f':
159 if (infile) {
160 help();
161 fprintf(stderr, "yangre error: multiple input files are not supported.\n");
162 goto cleanup;
163 } else if (patterns_count) {
164 help();
165 fprintf(stderr, "yangre error: command line patterns cannot be mixed with file input.\n");
166 goto cleanup;
167 }
Jan Kundrátb1aa77f2021-12-13 15:16:47 +0100168 infile = fopen(optarg, "rb");
Radek Krejci5819f7c2019-05-31 14:53:29 +0200169 if (!infile) {
170 fprintf(stderr, "yangre error: unable to open input file %s (%s).\n", optarg, strerror(errno));
171 goto cleanup;
172 }
173
Radek Krejci4001a102020-11-13 15:43:05 +0100174 while ((l = getline(&str, &len, infile)) != -1) {
175 if (!blankline && (str[0] == '\n')) {
Radek Krejci5819f7c2019-05-31 14:53:29 +0200176 /* blank line */
177 blankline = 1;
178 continue;
179 }
Radek Krejci4001a102020-11-13 15:43:05 +0100180 if ((str[0] != '\n') && (str[l - 1] == '\n')) {
Radek Krejci5819f7c2019-05-31 14:53:29 +0200181 /* remove ending newline */
182 str[l - 1] = '\0';
183 }
184 if (blankline) {
185 /* done - str is now the string to check */
186 blankline = 0;
187 break;
188 /* else read the patterns */
189 } else if (add_pattern(&patterns, &invert_match, &patterns_count,
Radek Krejci4001a102020-11-13 15:43:05 +0100190 (str[0] == ' ') ? &str[1] : str)) {
Radek Krejci5819f7c2019-05-31 14:53:29 +0200191 goto cleanup;
192 }
193 if (str[0] == ' ') {
194 /* set invert-match */
195 invert_match[patterns_count - 1] = 1;
196 }
197 }
198 if (blankline) {
199 /* corner case, no input after blankline meaning the pattern to check is empty */
200 if (str != NULL) {
201 free(str);
202 }
203 str = malloc(sizeof(char));
204 str[0] = '\0';
205 }
206 break;
207 case 'i':
208 if (!patterns_count || invert_match[patterns_count - 1]) {
209 help();
210 fprintf(stderr, "yangre error: invert-match option must follow some pattern.\n");
211 goto cleanup;
212 }
213 invert_match[patterns_count - 1] = 1;
214 break;
215 case 'p':
216 if (infile) {
217 help();
218 fprintf(stderr, "yangre error: command line patterns cannot be mixed with file input.\n");
219 goto cleanup;
220 }
221 if (add_pattern(&patterns, &invert_match, &patterns_count, optarg)) {
222 goto cleanup;
223 }
224 break;
225 case 'v':
226 version();
aPiecekbfdf83b2023-06-29 13:55:09 +0200227 info_printed = 1;
Radek Krejci5819f7c2019-05-31 14:53:29 +0200228 break;
229 case 'V':
230 verbose = 1;
231 break;
232 default:
233 help();
234 if (optopt) {
235 fprintf(stderr, "yangre error: invalid option: -%c\n", optopt);
236 } else {
237 fprintf(stderr, "yangre error: invalid option: %s\n", argv[optind - 1]);
238 }
239 goto cleanup;
240 }
241 }
242
aPiecekbfdf83b2023-06-29 13:55:09 +0200243 if (info_printed) {
244 ret = 0;
Radek Krejci5819f7c2019-05-31 14:53:29 +0200245 goto cleanup;
246 }
247
248 if (!str) {
249 /* check options compatibility */
250 if (optind >= argc) {
251 help();
252 fprintf(stderr, "yangre error: missing <string> parameter to process.\n");
253 goto cleanup;
254 } else if (!patterns_count) {
255 help();
256 fprintf(stderr, "yangre error: missing pattern parameter to use.\n");
257 goto cleanup;
258 }
259 str = argv[optind];
260 }
261
Radek Krejci4001a102020-11-13 15:43:05 +0100262 for (modstr = (char *)module_start, i = 0; i < patterns_count; i++) {
Radek Krejci5819f7c2019-05-31 14:53:29 +0200263 if (asprintf(&s, "%s pattern %s%s", modstr, patterns[i], invert_match[i] ? module_invertmatch : module_match) == -1) {
264 fprintf(stderr, "yangre error: memory allocation failed.\n");
265 goto cleanup;
266 }
267 if (modstr != module_start) {
268 free(modstr);
269 }
270 modstr = s;
271 }
272 if (asprintf(&s, "%s%s", modstr, module_end) == -1) {
273 fprintf(stderr, "yangre error: memory allocation failed.\n");
274 goto cleanup;
275 }
276 if (modstr != module_start) {
277 free(modstr);
278 }
279 modstr = s;
280
281 if (ly_ctx_new(NULL, 0, &ctx)) {
282 goto cleanup;
283 }
284
285 ly_set_log_clb(pattern_error, 0);
Michal Vasko3a41dff2020-07-15 14:30:28 +0200286 if (lys_parse_mem(ctx, modstr, LYS_IN_YANG, &mod) || !mod->compiled || !mod->compiled->data) {
Radek Krejci5819f7c2019-05-31 14:53:29 +0200287 goto cleanup;
288 }
289
Radek Krejcid7857262020-11-06 10:29:22 +0100290 /* check the value */
Michal Vaskoaebbce02021-04-06 09:23:37 +0200291 match = lyd_value_validate(ctx, mod->compiled->data, str, strlen(str), NULL, NULL, NULL);
Radek Krejcid7857262020-11-06 10:29:22 +0100292
Radek Krejci5819f7c2019-05-31 14:53:29 +0200293 if (verbose) {
294 for (i = 0; i < patterns_count; i++) {
295 fprintf(stdout, "pattern %d: %s\n", i + 1, patterns[i]);
296 fprintf(stdout, "matching %d: %s\n", i + 1, invert_match[i] ? "inverted" : "regular");
297 }
298 fprintf(stdout, "string : %s\n", str);
299 if (match == LY_SUCCESS) {
300 fprintf(stdout, "result : matching\n");
301 } else if (match == LY_EVALID) {
302 fprintf(stdout, "result : not matching\n");
303 } else {
Radek Krejcid7857262020-11-06 10:29:22 +0100304 fprintf(stdout, "result : error (%s)\n", ly_errmsg(ctx));
Radek Krejci5819f7c2019-05-31 14:53:29 +0200305 }
306 }
307 if (match == LY_SUCCESS) {
308 ret = 0;
309 } else if (match == LY_EVALID) {
aPiecekbfdf83b2023-06-29 13:55:09 +0200310 ret = 2;
Radek Krejci5819f7c2019-05-31 14:53:29 +0200311 } else {
aPiecekbfdf83b2023-06-29 13:55:09 +0200312 ret = 1;
Radek Krejci5819f7c2019-05-31 14:53:29 +0200313 }
314
315cleanup:
Radek Krejci90ed21e2021-04-12 14:47:46 +0200316 ly_ctx_destroy(ctx);
Radek Krejci5819f7c2019-05-31 14:53:29 +0200317 for (i = 0; i < patterns_count; i++) {
318 free(patterns[i]);
319 }
320 free(patterns);
321 free(invert_match);
aPiecekb0356f42023-06-22 10:13:38 +0200322 if (modstr != module_start) {
323 free(modstr);
324 }
Radek Krejci5819f7c2019-05-31 14:53:29 +0200325 if (infile) {
326 fclose(infile);
327 free(str);
328 }
Radek Krejci5819f7c2019-05-31 14:53:29 +0200329
330 return ret;
331}