/** * @file main.c * @author Michal Vasko * @brief libyang's yang2yin converter tool * * Copyright (c) 2016 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://opensource.org/licenses/BSD-3-Clause */ #include #include #include #include #include #include #define INDENT(x) (x ? " " : "") #define LEVEL(x) (x * 2) enum yang_arg { Y_NO_ARG, Y_IDENTIF_ARG, Y_STR_ARG }; enum yang_token { YANG_UNKNOWN = 0, YANG_ANYXML, YANG_ARGUMENT, YANG_AUGMENT, YANG_BASE, YANG_BELONGS_TO, YANG_BIT, YANG_CASE, YANG_CHOICE, YANG_CONFIG, YANG_CONTACT, YANG_CONTAINER, YANG_DEFAULT, YANG_DESCRIPTION, YANG_DEVIATE, YANG_DEVIATION, YANG_ENUM, YANG_ERROR_APP_TAG, YANG_ERROR_MESSAGE, YANG_EXTENSION, YANG_FEATURE, YANG_FRACTION_DIGITS, YANG_GROUPING, YANG_IDENTITY, YANG_IF_FEATURE, YANG_IMPORT, YANG_INCLUDE, YANG_INPUT, YANG_KEY, YANG_LEAF, YANG_LEAF_LIST, YANG_LENGTH, YANG_LIST, YANG_MANDATORY, YANG_MAX_ELEMENTS, YANG_MIN_ELEMENTS, YANG_MODULE, YANG_MUST, YANG_NAMESPACE, YANG_NOTIFICATION, YANG_ORDERED_BY, YANG_ORGANIZATION, YANG_OUTPUT, YANG_PATH, YANG_PATTERN, YANG_POSITION, YANG_PREFIX, YANG_PRESENCE, YANG_RANGE, YANG_REFERENCE, YANG_REFINE, YANG_REQUIRE_INSTANCE, YANG_REVISION, YANG_REVISION_DATE, YANG_RPC, YANG_STATUS, YANG_SUBMODULE, YANG_TYPE, YANG_TYPEDEF, YANG_UNIQUE, YANG_UNITS, YANG_USES, YANG_VALUE, YANG_WHEN, YANG_YANG_VERSION, YANG_YIN_ELEMENT, YANG_SEMICOLON, YANG_LEFT_BRACE, YANG_RIGHT_BRACE, YANG_CUSTOM /* simple extension with prefix */ }; static const char * keyword2str(enum yang_token keyword) { switch (keyword) { case YANG_ANYXML: return "anyxml"; case YANG_ARGUMENT: return "argument"; case YANG_AUGMENT: return "augment"; case YANG_BASE: return "base"; case YANG_BELONGS_TO: return "belongs-to"; case YANG_BIT: return "bit"; case YANG_CASE: return "case"; case YANG_CHOICE: return "choice"; case YANG_CONFIG: return "config"; case YANG_CONTACT: return "contact"; case YANG_CONTAINER: return "container"; case YANG_DEFAULT: return "default"; case YANG_DESCRIPTION: return "description"; case YANG_DEVIATE: return "deviate"; case YANG_DEVIATION: return "deviation"; case YANG_ENUM: return "enum"; case YANG_ERROR_APP_TAG: return "error-app-tag"; case YANG_ERROR_MESSAGE: return "error-message"; case YANG_EXTENSION: return "extension"; case YANG_FEATURE: return "feature"; case YANG_FRACTION_DIGITS: return "fraction-digits"; case YANG_GROUPING: return "grouping"; case YANG_IDENTITY: return "identitiy"; case YANG_IF_FEATURE: return "if-feature"; case YANG_IMPORT: return "import"; case YANG_INCLUDE: return "include"; case YANG_INPUT: return "input"; case YANG_KEY: return "key"; case YANG_LEAF: return "leaf"; case YANG_LEAF_LIST: return "leaf-list"; case YANG_LENGTH: return "length"; case YANG_LIST: return "list"; case YANG_MANDATORY: return "mandatory"; case YANG_MAX_ELEMENTS: return "max-elements"; case YANG_MIN_ELEMENTS: return "min-elements"; case YANG_MODULE: return "module"; case YANG_MUST: return "must"; case YANG_NAMESPACE: return "namespace"; case YANG_NOTIFICATION: return "notification"; case YANG_ORDERED_BY: return "ordered-by"; case YANG_ORGANIZATION: return "organization"; case YANG_OUTPUT: return "output"; case YANG_PATH: return "path"; case YANG_PATTERN: return "pattern"; case YANG_POSITION: return "position"; case YANG_PREFIX: return "prefix"; case YANG_PRESENCE: return "presence"; case YANG_RANGE: return "range"; case YANG_REFERENCE: return "reference"; case YANG_REFINE: return "refine"; case YANG_REQUIRE_INSTANCE: return "require-instance"; case YANG_REVISION: return "revision"; case YANG_REVISION_DATE: return "revision-date"; case YANG_RPC: return "rpc"; case YANG_STATUS: return "status"; case YANG_SUBMODULE: return "submodule"; case YANG_TYPE: return "type"; case YANG_TYPEDEF: return "typedef"; case YANG_UNIQUE: return "unique"; case YANG_UNITS: return "units"; case YANG_USES: return "uses"; case YANG_VALUE: return "value"; case YANG_WHEN: return "when"; case YANG_YANG_VERSION: return "yang-version"; case YANG_YIN_ELEMENT: return "yin-element"; case YANG_SEMICOLON: return ";"; case YANG_LEFT_BRACE: return "{"; case YANG_RIGHT_BRACE: return "}"; default: return ""; } } static int store_char(char c, char **buf, int *buf_len, int *used) { char *new_buf; if (*used == *buf_len) { *buf_len += 16; new_buf = realloc(*buf, *buf_len * sizeof *new_buf); if (!new_buf) { fprintf(stderr, "Memory allocation error (%s).\n", strerror(errno)); return -1; } *buf = new_buf; } (*buf)[*used] = c; ++(*used); return 0; } static FILE * open_module(const char *name, const char *search_dir) { DIR *dir; struct dirent *dirent; char *path; FILE *ret = NULL; dir = opendir(search_dir); if (!dir) { fprintf(stderr, "Opening the directory \"%s\" failed (%s).\n", search_dir, strerror(errno)); return NULL; } while ((dirent = readdir(dir))) { if (!strncmp(dirent->d_name, name, strlen(name)) && ((dirent->d_name[strlen(name)] == '.') || (dirent->d_name[strlen(name)] == '@')) && !strcmp(dirent->d_name + strlen(dirent->d_name) - 5, ".yang")) { path = malloc(strlen(search_dir) + 1 + strlen(dirent->d_name) + 1); if (!path) { fprintf(stderr, "Memory allocation error (%s).\n", strerror(errno)); break; } sprintf(path, "%s/%s", search_dir, dirent->d_name); ret = fopen(path, "r"); free(path); if (!ret) { fprintf(stderr, "Opening file \"%s\" failed (%s).\n", dirent->d_name, strerror(errno)); } break; } } closedir(dir); return ret; } /* read another word - character sequence without whitespaces (logs directly) * - words are basically YANG tokens * - there can be whitespaces if they are a part of string * - strings are always returned separately even if not separated by whitespaces * - strings are returned without ' or " * - strings divided by + are returned concatenated * - comments are skipped */ static char * get_word(FILE *in, char **buf, int *buf_len) { char c; /* comment: 0 - nothing, 1 - in line comment, 2 - in block comment, 3 - in block comment with last read character '*' * slash: 0 - nothing, 1 - last character was '/' * string: 0 - nothing, 1 - string with ', 2 - string with ", 3 - string with " with last character \, * 4 - string finished, now skipping whitespaces looking for +, * 5 - string continues after +, skipping whitespaces * indent: number of spaces of the indent, needed for double-quoted string, if following * str_nl_indent: can be non-zero only if string == 2, number of spaces to skip as the indent */ int used = 0, ret, slash = 0, comment = 0, string = 0, indent = 0, str_nl_indent = 0; do { ret = fread(&c, sizeof c, 1, in); if (ret < 1) { if (feof(in)) { break; } else if (ferror(in)) { fprintf(stderr, "Read error (%s).\n", strerror(errno)); } else { fprintf(stderr, "Int error (%d).\n", __LINE__); } return NULL; } if (string == 4) { if (c == '+') { /* string continues */ string = 5; } else if (!isspace(c)) { /* nope, string is finished */ if (ungetc(c, in) != c) { fprintf(stderr, "ungetc() failed.\n"); return NULL; } break; } } else if (string == 5) { if (!isspace(c)) { if (c == '\'') { string = 1; } else if (c == '\"') { string = 2; } else { /* it must be quoted again */ fprintf(stderr, "Both string parts divided by '+' must be quoted.\n"); return NULL; } } } else if (((c == '\'') || (c == '\"')) && !string) { if (used) { /* we want strings always in a separate word, leave it */ if (ungetc(c, in) != c) { fprintf(stderr, "ungetc() failed.\n"); return NULL; } break; } if (c == '\'') { string = 1; } else { string = 2; /* for the " itself */ ++indent; } } else if (comment) { switch (comment) { case 1: if (c == '\n') { comment = 0; } break; case 2: if (c == '*') { comment = 3; } break; case 3: if (c == '/') { comment = 0; } else { comment = 2; } break; default: fprintf(stderr, "Int error (%d).\n", __LINE__); return NULL; } } else if (slash) { if (c == '/') { comment = 1; } else if (c == '*') { comment = 2; } else { if (store_char('/', buf, buf_len, &used) || store_char(c, buf, buf_len, &used)) { return NULL; } } slash = 0; } else if ((c == '/') && !string) { slash = 1; } else if (((string == 1) && (c == '\'')) || ((string == 2) && (c == '\"'))) { /* string may be finished, but check for + */ string = 4; } else if ((string == 2) && (c == '\\')) { /* special character following */ string = 3; } else if (((c == ';') || (c == '{') || (c == '}')) && !string && used) { /* another keyword, leave it for later */ if (ungetc(c, in) != c) { fprintf(stderr, "ungetc() failed.\n"); return NULL; } break; } else if (!string && !used && ((c == ' ') || (c == '\t'))) { if (c == ' ') { ++indent; } else { indent += 8; } } else if (!isspace(c) || string) { if (string == 2) { if (isspace(c)) { /* skipping indent spaces */ if (str_nl_indent && (c != '\n')) { if (c == ' ') { --str_nl_indent; } else if (c == '\t') { if (str_nl_indent < 9) { str_nl_indent = 0; } else { str_nl_indent -= 8; } } /* we just ignore other whitespace characters */ continue; } else if (c == '\n') { str_nl_indent = indent; } } else if (str_nl_indent) { /* first non-whitespace character, clear current indent */ str_nl_indent = 0; } } else if (string == 3) { /* string encoded characters */ switch (c) { case 'n': c = '\n'; break; case 't': c = '\t'; break; case '\"': /* c is correct */ break; case '\\': /* c is correct */ break; default: /* so let this one slide just like pyang and yanglint >:-| fprintf(stderr, "Double-quoted string unknown special character '\\%c'.\n", c); return NULL;*/ if (store_char('\\', buf, buf_len, &used)) { return NULL; } } string = 2; } if (store_char(c, buf, buf_len, &used)) { return NULL; } /* this is a keyword */ if (((c == ';') || (c == '{') || (c == '}')) && !string) { break; } } } while (!isspace(c) || !used || string); if (store_char('\0', buf, buf_len, &used)) { return NULL; } return *buf; } /* (does not log) */ static enum yang_token get_keyword(char *word, enum yang_arg *arg) { enum yang_token ret = YANG_UNKNOWN; switch (word[0]) { case 'a': ++word; if (!strncmp(word, "nyxml", 5)) { word += 5; ret = YANG_ANYXML; *arg = Y_IDENTIF_ARG; } else if (!strncmp(word, "rgument", 7)) { word += 7; ret = YANG_ARGUMENT; *arg = Y_STR_ARG; } else if (!strncmp(word, "ugment", 6)) { word += 6; ret = YANG_AUGMENT; *arg = Y_STR_ARG; } break; case 'b': ++word; if (!strncmp(word, "ase", 3)) { word += 3; ret = YANG_BASE; *arg = Y_STR_ARG; } else if (!strncmp(word, "elongs-to", 9)) { word += 9; ret = YANG_BELONGS_TO; *arg = Y_IDENTIF_ARG; } else if (!strncmp(word, "it", 2)) { word += 2; ret = YANG_BIT; *arg = Y_STR_ARG; } break; case 'c': ++word; if (!strncmp(word, "ase", 3)) { word += 3; ret = YANG_CASE; *arg = Y_IDENTIF_ARG; } else if (!strncmp(word, "hoice", 5)) { word += 5; ret = YANG_CHOICE; *arg = Y_IDENTIF_ARG; } else if (!strncmp(word, "on", 2)) { word += 2; if (!strncmp(word, "fig", 3)) { word += 3; ret = YANG_CONFIG; *arg = Y_STR_ARG; } else if (!strncmp(word, "ta", 2)) { word += 2; if (!strncmp(word, "ct", 2)) { word += 2; ret = YANG_CONTACT; *arg = Y_STR_ARG; } else if (!strncmp(word, "iner", 4)) { word += 4; ret = YANG_CONTAINER; *arg = Y_IDENTIF_ARG; } } } break; case 'd': ++word; if (word[0] == 'e') { ++word; if (!strncmp(word, "fault", 5)) { word += 5; ret = YANG_DEFAULT; *arg = Y_STR_ARG; } else if (!strncmp(word, "scription", 9)) { word += 9; ret = YANG_DESCRIPTION; *arg = Y_STR_ARG; } else if (!strncmp(word, "viat", 4)) { word += 4; if (word[0] == 'e') { ++word; ret = YANG_DEVIATE; *arg = Y_STR_ARG; } else if (!strncmp(word, "ion", 3)) { word += 3; ret = YANG_DEVIATION; *arg = Y_STR_ARG; } } } break; case 'e': ++word; if (!strncmp(word, "num", 3)) { word += 3; ret = YANG_ENUM; *arg = Y_STR_ARG; } else if (!strncmp(word, "rror-", 5)) { word += 5; if (!strncmp(word, "app-tag", 7)) { word += 7; ret = YANG_ERROR_APP_TAG; *arg = Y_STR_ARG; } else if (!strncmp(word, "message", 7)) { word += 7; ret = YANG_ERROR_MESSAGE; *arg = Y_STR_ARG; } } else if (!strncmp(word, "xtension", 8)) { word += 8; ret = YANG_EXTENSION; *arg = Y_IDENTIF_ARG; } break; case 'f': ++word; if (!strncmp(word, "eature", 6)) { word += 6; ret = YANG_FEATURE; *arg = Y_IDENTIF_ARG; } else if (!strncmp(word, "raction-digits", 14)) { word += 14; ret = YANG_FRACTION_DIGITS; *arg = Y_STR_ARG; } break; case 'g': ++word; if (!strncmp(word, "rouping", 7)) { word += 7; ret = YANG_GROUPING; *arg = Y_IDENTIF_ARG; } break; case 'i': ++word; if (!strncmp(word, "dentity", 7)) { word += 7; ret = YANG_IDENTITY; *arg = Y_IDENTIF_ARG; } else if (!strncmp(word, "f-feature", 9)) { word += 9; ret = YANG_IF_FEATURE; *arg = Y_IDENTIF_ARG; } else if (!strncmp(word, "mport", 5)) { word += 5; ret = YANG_IMPORT; *arg = Y_IDENTIF_ARG; } else if (word[0] == 'n') { ++word; if (!strncmp(word, "clude", 5)) { word += 5; ret = YANG_INCLUDE; *arg = Y_IDENTIF_ARG; } else if (!strncmp(word, "put", 3)) { word += 3; ret = YANG_INPUT; *arg = Y_NO_ARG; } } break; case 'k': ++word; if (!strncmp(word, "ey", 2)) { word += 2; ret = YANG_KEY; *arg = Y_STR_ARG; } break; case 'l': ++word; if (word[0] == 'e') { ++word; if (!strncmp(word, "af", 2)) { word += 2; if (word[0] != '-') { ret = YANG_LEAF; *arg = Y_IDENTIF_ARG; } else if (!strncmp(word, "-list", 5)) { word += 5; ret = YANG_LEAF_LIST; *arg = Y_IDENTIF_ARG; } } else if (!strncmp(word, "ngth", 4)) { word += 4; ret = YANG_LENGTH; *arg = Y_STR_ARG; } } else if (!strncmp(word, "ist", 3)) { word += 3; ret = YANG_LIST; *arg = Y_IDENTIF_ARG; } break; case 'm': ++word; if (word[0] == 'a') { ++word; if (!strncmp(word, "ndatory", 7)) { word += 7; ret = YANG_MANDATORY; *arg = Y_STR_ARG; } else if (!strncmp(word, "x-elements", 10)) { word += 10; ret = YANG_MAX_ELEMENTS; *arg = Y_STR_ARG; } } else if (!strncmp(word, "in-elements", 11)) { word += 11; ret = YANG_MIN_ELEMENTS; *arg = Y_STR_ARG; } else if (!strncmp(word, "odule", 5)) { word += 5; ret = YANG_MODULE; *arg = Y_IDENTIF_ARG; } else if (!strncmp(word, "ust", 3)) { word += 3; ret = YANG_MUST; *arg = Y_STR_ARG; } break; case 'n': ++word; if (!strncmp(word, "amespace", 8)) { word += 8; ret = YANG_NAMESPACE; *arg = Y_STR_ARG; } else if (!strncmp(word, "otification", 11)) { word += 11; ret = YANG_NOTIFICATION; *arg = Y_IDENTIF_ARG; } break; case 'o': ++word; if (word[0] == 'r') { ++word; if (!strncmp(word, "dered-by", 8)) { word += 8; ret = YANG_ORDERED_BY; *arg = Y_STR_ARG; } else if (!strncmp(word, "ganization", 10)) { word += 10; ret = YANG_ORGANIZATION; *arg = Y_STR_ARG; } } else if (!strncmp(word, "utput", 5)) { word += 5; ret = YANG_OUTPUT; *arg = Y_NO_ARG; } break; case 'p': ++word; if (!strncmp(word, "at", 2)) { word += 2; if (word[0] == 'h') { ++word; ret = YANG_PATH; *arg = Y_STR_ARG; } else if (!strncmp(word, "tern", 4)) { word += 4; ret = YANG_PATTERN; *arg = Y_STR_ARG; } } else if (!strncmp(word, "osition", 7)) { word += 7; ret = YANG_POSITION; *arg = Y_STR_ARG; } else if (!strncmp(word, "re", 2)) { word += 2; if (!strncmp(word, "fix", 3)) { word += 3; ret = YANG_PREFIX; *arg = Y_STR_ARG; } else if (!strncmp(word, "sence", 5)) { word += 5; ret = YANG_PRESENCE; *arg = Y_STR_ARG; } } break; case 'r': ++word; if (!strncmp(word, "ange", 4)) { word += 4; ret = YANG_RANGE; *arg = Y_STR_ARG; } else if (word[0] == 'e') { ++word; if (word[0] == 'f') { ++word; if (!strncmp(word, "erence", 6)) { word += 6; ret = YANG_REFERENCE; *arg = Y_STR_ARG; } else if (!strncmp(word, "ine", 3)) { word += 3; ret = YANG_REFINE; *arg = Y_STR_ARG; } } else if (!strncmp(word, "quire-instance", 14)) { word += 14; ret = YANG_REQUIRE_INSTANCE; *arg = Y_STR_ARG; } else if (!strncmp(word, "vision", 6)) { word += 6; if (word[0] != '-') { ret = YANG_REVISION; *arg = Y_STR_ARG; } else if (!strncmp(word, "-date", 5)) { word += 5; ret = YANG_REVISION_DATE; *arg = Y_STR_ARG; } } } else if (!strncmp(word, "pc", 2)) { word += 2; ret = YANG_RPC; *arg = Y_IDENTIF_ARG; } break; case 's': ++word; if (!strncmp(word, "tatus", 5)) { word += 5; ret = YANG_STATUS; *arg = Y_STR_ARG; } else if (!strncmp(word, "ubmodule", 8)) { word += 8; ret = YANG_SUBMODULE; *arg = Y_IDENTIF_ARG; } break; case 't': ++word; if (!strncmp(word, "ype", 3)) { word += 3; if (word[0] != 'd') { ret = YANG_TYPE; *arg = Y_STR_ARG; } else if (!strncmp(word, "def", 3)) { word += 3; ret = YANG_TYPEDEF; *arg = Y_IDENTIF_ARG; } } break; case 'u': ++word; if (!strncmp(word, "ni", 2)) { word += 2; if (!strncmp(word, "que", 3)) { word += 3; ret = YANG_UNIQUE; *arg = Y_STR_ARG; } else if (!strncmp(word, "ts", 2)) { word += 2; ret = YANG_UNITS; *arg = Y_STR_ARG; } } else if (!strncmp(word, "ses", 3)) { word += 3; ret = YANG_USES; *arg = Y_IDENTIF_ARG; } break; case 'v': ++word; if (!strncmp(word, "alue", 4)) { word += 4; ret = YANG_VALUE; *arg = Y_STR_ARG; } break; case 'w': ++word; if (!strncmp(word, "hen", 3)) { word += 3; ret = YANG_WHEN; *arg = Y_STR_ARG; } break; case 'y': ++word; if (!strncmp(word, "ang-version", 11)) { word += 11; ret = YANG_YANG_VERSION; *arg = Y_STR_ARG; } else if (!strncmp(word, "in-element", 10)) { word += 10; ret = YANG_YIN_ELEMENT; *arg = Y_STR_ARG; } break; case ';': ++word; ret = YANG_SEMICOLON; *arg = Y_NO_ARG; break; case '{': ++word; ret = YANG_LEFT_BRACE; *arg = Y_NO_ARG; break; case '}': ++word; ret = YANG_RIGHT_BRACE; *arg = Y_NO_ARG; break; default: break; } if ((ret == YANG_UNKNOWN) && strchr(word, ':') && (strchr(word, ':') == strrchr(word, ':'))) { /* some string with one ':', let's say it's an extension */ word += strlen(word); ret = YANG_CUSTOM; *arg = Y_NO_ARG; } /* not a keyword after all */ if ((ret != YANG_UNKNOWN) && word[0]) { ret = YANG_UNKNOWN; } return ret; } static void print_text_xml_encode(FILE *out, const char *word) { const char *ptr; for (ptr = word; ptr[0]; ++ptr) { switch (ptr[0]) { case '<': fputs("<", out); break; case '>': fputs(">", out); break; case '&': fputs("&", out); break; /* not really necessary, I guess... */ /*case '\'': fputs("'", out); break; case '\"': fputs(""", out); break;*/ default: fputc(ptr[0], out); break; } } } static void print_attr_val_xml_encode(FILE *out, const char *word) { const char *ptr; for (ptr = word; ptr[0]; ++ptr) { switch (ptr[0]) { case '<': fputs("<", out); break; case '>': fputs(">", out); break; case '&': fputs("&", out); break; /* we always use ", so ' is fine */ /*case '\'': fputs("'", out); break;*/ case '\"': fputs(""", out); break; default: fputc(ptr[0], out); break; } } } static int print_keyword(enum yang_token keyword, enum yang_arg arg, FILE *out, int level, FILE *in, char **buf, int *buf_len) { char *word; const char *yin_element = NULL, *close_tag; switch (keyword) { case YANG_ANYXML: fprintf(out, "%*s\n", LEVEL(level), INDENT(level)); yin_element = "text"; close_tag = "contact"; break; case YANG_CONTAINER: fprintf(out, "%*s\n", LEVEL(level), INDENT(level)); yin_element = "text"; close_tag = "description"; break; case YANG_DEVIATE: fprintf(out, "%*s\n", LEVEL(level), INDENT(level)); yin_element = "value"; close_tag = "error-message"; break; case YANG_EXTENSION: fprintf(out, "%*s\n", LEVEL(level), INDENT(level)); yin_element = "text"; close_tag = "organization"; break; case YANG_OUTPUT: fprintf(out, "%*s\n", LEVEL(level), INDENT(level)); yin_element = "text"; close_tag = "reference"; break; case YANG_REFINE: fprintf(out, "%*s", LEVEL(level), INDENT(level), yin_element); } /* keyword argument */ switch (arg) { case Y_IDENTIF_ARG: word = get_word(in, buf, buf_len); if (!word) { return -1; } /* word is the identifier (after some changes) */ fprintf(out, "%s%s", word, (yin_element ? "" : "\"")); break; case Y_STR_ARG: word = get_word(in, buf, buf_len); if (!word) { return -1; } /* word is the string */ if (yin_element) { print_text_xml_encode(out, word); } else { print_attr_val_xml_encode(out, word); } if (!yin_element) { fprintf(out, "\""); } break; case Y_NO_ARG: break; } if (yin_element) { fprintf(out, "\n", yin_element); --level; } word = get_word(in, buf, buf_len); if (!word) { return -1; } keyword = get_keyword(word, &arg); if (!keyword) { fprintf(stderr, "Unexpected characters (\"%.20s\"...).\n", word); return -1; } if (keyword == YANG_LEFT_BRACE) { if (!yin_element) { fprintf(out, ">\n"); } while (1) { word = get_word(in, buf, buf_len); if (!word) { return -1; } keyword = get_keyword(word, &arg); if (!keyword) { fprintf(stderr, "Unexpected characters (\"%.20s\"...).\n", word); return -1; } if (keyword == YANG_RIGHT_BRACE) { break; } if (print_keyword(keyword, arg, out, level + 1, in, buf, buf_len)) { return -1; } } fprintf(out, "%*s\n", LEVEL(level), INDENT(level), close_tag); } if ((keyword != YANG_SEMICOLON) && (keyword != YANG_RIGHT_BRACE)) { fprintf(stderr, "Unexpected keyword \"%s\".\n", keyword2str(keyword)); return -1; } if (keyword == YANG_SEMICOLON) { if (yin_element) { fprintf(out, "%*s\n", LEVEL(level), INDENT(level), close_tag); } else { fprintf(out, "/>\n"); } } return 0; } static int print_sub_module(enum yang_token keyword, enum yang_arg arg, FILE *out, FILE *in, char **buf, int *buf_len, const char *namespace, const char *prefix, char **import_namespaces, char **import_prefixes, int import_count) { char *word; const char *close_tag; int i; switch (keyword) { case YANG_MODULE: fprintf(out, "\n"); while (1) { word = get_word(in, buf, buf_len); if (!word) { return -1; } keyword = get_keyword(word, &arg); if (!keyword) { fprintf(stderr, "Unexpected characters (\"%.20s\"...).\n", word); return -1; } if (keyword == YANG_RIGHT_BRACE) { break; } if (print_keyword(keyword, arg, out, 1, in, buf, buf_len)) { return -1; } } fprintf(out, "\n", close_tag); return 0; } static int find_namespace_imports(FILE *in, char **buf, int *buf_len, char **name_space, char **prefix, char ***import_modules, char ***import_prefixes, int *import_count) { char *word; void *new_buf; enum yang_token keyword; enum yang_arg arg; int i, want_prefix = 0; *name_space = NULL; if (prefix) { *prefix = NULL; } if (import_modules && import_prefixes && import_count) { *import_modules = NULL; *import_prefixes = NULL; *import_count = 0; } while (1) { word = get_word(in, buf, buf_len); if (!word) { goto error; } else if (!word[0]) { /* EOF */ goto success; } keyword = get_keyword(word, &arg); if (!keyword) { fprintf(stderr, "Unexpected characters (\"%.20s\"...).\n", word); goto error; } switch (arg) { case Y_IDENTIF_ARG: case Y_STR_ARG: word = get_word(in, buf, buf_len); if (!word) { goto error; } break; case Y_NO_ARG: break; } switch (keyword) { case YANG_NAMESPACE: /* module */ if (want_prefix) { fprintf(stderr, "Unexpected keyword \"%s\", expected \"prefix\".\n", keyword2str(keyword)); goto error; } if (*name_space) { fprintf(stderr, "Unexpected keyword \"%s\", already encountered.\n", keyword2str(keyword)); goto error; } *name_space = strdup(word); if (!*name_space) { fprintf(stderr, "Memory allocation error (%s).\n", strerror(errno)); goto error; } break; case YANG_IMPORT: if (want_prefix) { fprintf(stderr, "Unexpected keyword \"%s\", expected \"prefix\".\n", keyword2str(keyword)); goto error; } if (import_modules && import_prefixes && import_count) { new_buf = realloc(*import_modules, (*import_count + 1) * sizeof **import_modules); if (!new_buf) { fprintf(stderr, "Memory allocation error (%s).\n", strerror(errno)); goto error; } *import_modules = new_buf; new_buf = realloc(*import_prefixes, (*import_count + 1) * sizeof **import_prefixes); if (!new_buf) { fprintf(stderr, "Memory allocation error (%s).\n", strerror(errno)); goto error; } *import_prefixes = new_buf; (*import_modules)[*import_count] = strdup(word); if (!(*import_modules)[*import_count]) { fprintf(stderr, "Memory allocation error (%s).\n", strerror(errno)); goto error; } (*import_prefixes)[*import_count] = NULL; ++(*import_count); } want_prefix = 1; break; case YANG_BELONGS_TO: /* submodule */ if (want_prefix) { fprintf(stderr, "Unexpected keyword \"%s\", expected \"prefix\".\n", keyword2str(keyword)); goto error; } if (*name_space) { fprintf(stderr, "Unexpected keyword \"%s\", already encountered.\n", keyword2str(keyword)); goto error; } *name_space = strdup(word); if (!*name_space) { fprintf(stderr, "Memory allocation error (%s).\n", strerror(errno)); goto error; } break; case YANG_PREFIX: if (!want_prefix) { /* main (sub)module prefix */ if (prefix) { *prefix = strdup(word); if (!*prefix) { fprintf(stderr, "Memory allocation error (%s).\n", strerror(errno)); goto error; } } } else { if (import_modules && import_prefixes && import_count) { (*import_prefixes)[*import_count - 1] = strdup(word); if (!(*import_prefixes)[*import_count - 1]) { fprintf(stderr, "Memory allocation error (%s).\n", strerror(errno)); goto error; } } want_prefix = 0; } break; case YANG_SEMICOLON: case YANG_LEFT_BRACE: case YANG_RIGHT_BRACE: case YANG_REVISION_DATE: /* the only ones that can appear before prefix */ break; case YANG_CONTACT: case YANG_DESCRIPTION: case YANG_REFERENCE: case YANG_REVISION: case YANG_EXTENSION: case YANG_FEATURE: case YANG_IDENTITY: case YANG_TYPEDEF: case YANG_GROUPING: case YANG_CONTAINER: case YANG_LEAF: case YANG_LEAF_LIST: case YANG_LIST: case YANG_CHOICE: case YANG_ANYXML: case YANG_USES: case YANG_AUGMENT: case YANG_RPC: case YANG_NOTIFICATION: case YANG_DEVIATION: /* no import can follow */ goto success; default: if (want_prefix) { fprintf(stderr, "Unexpected keyword \"%s\", expected \"prefix\".\n", keyword2str(keyword)); goto error; } break; } } success: if (want_prefix) { fprintf(stderr, "Unexpected EOF/keyword, expected \"prefix\".\n"); goto error; } if (prefix && !(*prefix)) { fprintf(stderr, "Module prefix/submodule \"belongs-to\" prefix not found.\n"); goto error; } if (!(*name_space)) { fprintf(stderr, "Module namespace/submodule \"belongs-to\" module not found.\n"); goto error; } return 0; error: if (import_modules && import_prefixes && import_count) { for (i = 0; i < *import_count; ++i) { free((*import_modules)[i]); free((*import_prefixes)[i]); } free(*import_modules); free(*import_prefixes); } if (prefix) { free(*prefix); } free(*name_space); return -1; } static int convert_yang2yin(FILE *out, FILE *in, const char *search_dir) { char *buf = NULL, *word, *name_space, *prefix, **import_modules, **import_prefixes; int buf_len = 0, ret = 0, import_count, i; enum yang_token keyword; enum yang_arg arg; FILE *mod_file = NULL; /* * 1st module parsing */ /* learn whther it's a module or submodule */ word = get_word(in, &buf, &buf_len); if (!word) { free(buf); return -1; } keyword = get_keyword(word, &arg); if (!keyword) { fprintf(stderr, "Unexpected characters (\"%.20s\"...).\n", word); free(buf); return -1; } if ((keyword != YANG_MODULE) && (keyword != YANG_SUBMODULE)) { fprintf(stderr, "Unexpected keyword \"%s\".\n", keyword2str(keyword)); free(buf); return -1; } word = get_word(in, &buf, &buf_len); if (!word) { free(buf); return -1; } /* learn namespace, imports of the (sub)module being converted */ if (find_namespace_imports(in, &buf, &buf_len, &name_space, &prefix, &import_modules, &import_prefixes, &import_count)) { free(buf); return -1; } /* learn the main module namespace */ if (keyword == YANG_SUBMODULE) { mod_file = open_module(name_space, search_dir); if (!mod_file) { fprintf(stderr, "Failed to open the module \"%s\".\n", name_space); ret = -1; goto cleanup; } free(name_space); name_space = NULL; if (find_namespace_imports(mod_file, &buf, &buf_len, &name_space, NULL, NULL, NULL, NULL)) { ret = -1; goto cleanup; } fclose(mod_file); mod_file = NULL; } /* learn the namespaces of import modules */ for (i = 0; i < import_count; ++i) { mod_file = open_module(import_modules[i], search_dir); if (!mod_file) { fprintf(stderr, "Failed to open the module \"%s\".\n", import_modules[i]); ret = -1; goto cleanup; } free(import_modules[i]); import_modules[i] = NULL; if (find_namespace_imports(mod_file, &buf, &buf_len, &import_modules[i], NULL, NULL, NULL, NULL)) { ret = -1; goto cleanup; } fclose(mod_file); mod_file = NULL; } rewind(in); /* * 2nd module parsing */ /* print xml header */ fprintf(out, "\n"); word = get_word(in, &buf, &buf_len); if (!word) { ret = -1; goto cleanup; } keyword = get_keyword(word, &arg); if (!keyword) { fprintf(stderr, "Unexpected characters (\"%.20s\"...).\n", word); ret = -1; goto cleanup; } if ((keyword == YANG_MODULE) || (keyword == YANG_SUBMODULE)) { ret = print_sub_module(keyword, arg, out, in, &buf, &buf_len, name_space, prefix, import_modules, import_prefixes, import_count); } else { fprintf(stderr, "Unexpected keyword \"%s\".\n", keyword2str(keyword)); ret = -1; goto cleanup; } cleanup: free(name_space); free(prefix); for (i = 0; i < import_count; ++i) { free(import_modules[i]); free(import_prefixes[i]); } free(import_modules); free(import_prefixes); if (mod_file) { fclose(mod_file); } free(buf); return ret; } int main(int argc, char **argv) { const char *in_file = NULL, *out_file = NULL, *search_dir = "."; char *ptr; FILE *input = NULL, *output = NULL; int ret = 1; switch (argc) { case 1: break; case 2: if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { fprintf(stdout, "Usage:\n\t%s [input-file] [output-file]\n", argv[0]); return 0; } in_file = argv[1]; break; case 3: in_file = argv[1]; out_file = argv[2]; break; default: fprintf(stderr, "Invalid arguments.\n"); return 1; } if (in_file) { input = fopen(in_file, "r"); if (!input) { fprintf(stderr, "Failed to open \"%s\" for reading (%s).\n", in_file, strerror(errno)); goto cleanup; } if ((ptr = strrchr(in_file, '/'))) { *ptr = '\0'; search_dir = in_file; } } if (out_file) { output = fopen(out_file, "w"); if (!input) { fprintf(stderr, "Failed to open \"%s\" for writing (%s).\n", out_file, strerror(errno)); goto cleanup; } } ret = convert_yang2yin(output ? output : stdout, input ? input : stdin, search_dir); cleanup: if (input) { fclose(input); } if (output) { fclose(output); } return ret; }