augeas-1.13.0/0000755000175000017500000000000014161102723010037 500000000000000augeas-1.13.0/augeas.spec0000644000175000017500000002156214161102706012107 00000000000000Name: augeas Version: 1.13.0 Release: 1%{?dist} Summary: A library for changing configuration files Group: System Environment/Libraries License: LGPLv2+ URL: http://augeas.net/ Source0: http://download.augeas.net/%{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: readline-devel libselinux-devel libxml2-devel Requires: %{name}-libs = %{version}-%{release} %description A library for programmatically editing configuration files. Augeas parses configuration files into a tree structure, which it exposes through its public API. Changes made through the API are written back to the initially read files. The transformation works very hard to preserve comments and formatting details. It is controlled by ``lens'' definitions that describe the file format and the transformation into a tree. %package devel Summary: Development files for %{name} Group: Development/Libraries Requires: %{name}-libs = %{version}-%{release} Requires: pkgconfig %description devel The %{name}-devel package contains libraries and header files for developing applications that use %{name}. %package libs Summary: Libraries for %{name} Group: System Environment/Libraries %description libs The libraries for %{name}. Augeas is a library for programmatically editing configuration files. It parses configuration files into a tree structure, which it exposes through its public API. Changes made through the API are written back to the initially read files. %package static Summary: Static libraries for %{name} Group: Development/Libraries Requires: %{name}-devel = %{version}-%{release} %description static The %{name}-static package contains static libraries needed to produce static builds using %{name}. %prep %setup -q %build %configure \ %ifarch riscv64 --disable-gnulib-tests \ %endif --enable-static make %{?_smp_mflags} %check # Disable test-preserve.sh SELinux testing. This fails when run under mock due # to differing SELinux labelling. export SKIP_TEST_PRESERVE_SELINUX=1 make %{?_smp_mflags} check || { echo '===== tests/test-suite.log =====' cat tests/test-suite.log exit 1 } %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT INSTALL="%{__install} -p" find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' # The tests/ subdirectory contains lenses used only for testing, and # so it shouldn't be packaged. rm -r $RPM_BUILD_ROOT%{_datadir}/augeas/lenses/dist/tests %clean rm -rf $RPM_BUILD_ROOT %post libs -p /sbin/ldconfig %postun libs -p /sbin/ldconfig %files %defattr(-,root,root,-) %{_bindir}/augtool %{_bindir}/augparse %{_bindir}/fadot %doc %{_mandir}/man1/* %{_datadir}/vim/vimfiles/syntax/augeas.vim %{_datadir}/vim/vimfiles/ftdetect/augeas.vim %files libs %defattr(-,root,root,-) # _datadir/augeas and _datadir/augeas/lenses are owned # by filesystem. %{_datadir}/augeas/lenses/dist %{_libdir}/*.so.* %doc AUTHORS COPYING NEWS %files devel %defattr(-,root,root,-) %doc %{_includedir}/* %{_libdir}/*.so %{_libdir}/pkgconfig/augeas.pc %files static %defattr(-,root,root,-) %{_libdir}/libaugeas.a %{_libdir}/libfa.a %changelog * Fri Mar 17 2017 David Lutterkort - 1.8.0-1 - add static subpackage * Fri Feb 10 2017 Fedora Release Engineering - 1.7.0-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild * Thu Jan 12 2017 Igor Gnatenko - 1.7.0-3 - Rebuild for readline 7.x * Sat Nov 12 2016 Richard W.M. Jones - 1.7.0-2 - riscv64: Disable gnulib tests on riscv64 architecture. * Wed Nov 09 2016 Dominic Cleal - 1.7.0-1 - Update to 1.7.0 * Mon Aug 08 2016 Dominic Cleal - 1.6.0-1 - Update to 1.6.0 * Thu May 12 2016 Dominic Cleal - 1.5.0-1 - Update to 1.5.0 * Wed Feb 03 2016 Fedora Release Engineering - 1.4.0-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild * Wed Jun 17 2015 Fedora Release Engineering - 1.4.0-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild * Tue Jun 02 2015 Dominic Cleal - 1.4.0-1 - Update to 1.4.0 * Sat Nov 08 2014 Dominic Cleal - 1.3.0-1 - Update to 1.3.0; remove all patches * Fri Aug 15 2014 Fedora Release Engineering - 1.2.0-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild * Sat Jun 07 2014 Fedora Release Engineering - 1.2.0-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild * Mon Mar 31 2014 Dominic Cleal - 1.2.0-2 - Add patch for Krb5, parse braces in values (RHBZ#1079444) * Wed Feb 12 2014 Dominic Cleal - 1.2.0-1 - Update to 1.2.0, add check section - Update source URL to download.augeas.net (RHBZ#996032) * Sat Aug 03 2013 Fedora Release Engineering - 1.1.0-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild * Wed Jun 19 2013 David Lutterkort - 1.1.0-1 - Update to 1.1.0; remove all patches * Tue Jun 18 2013 Richard W.M. Jones - 1.0.0-4 - Fix /etc/sysconfig/network (RHBZ#904222). * Wed Jun 5 2013 Richard W.M. Jones - 1.0.0-3 - Don't package lenses in tests/ subdirectory. * Wed Feb 13 2013 Fedora Release Engineering - 1.0.0-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild * Fri Jan 4 2013 David Lutterkort - 1.0.0-1 - New version; remove all patches * Wed Jul 18 2012 Fedora Release Engineering - 0.10.0-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild * Tue Jan 10 2012 David Lutterkort - 0.10.0-3 - Add patches for bugs 247 and 248 (JSON lens) * Sat Dec 3 2011 Richard W.M. Jones - 0.10.0-2 - Add patch to resolve missing libxml2 requirement in augeas.pc. * Fri Dec 2 2011 David Lutterkort - 0.10.0-1 - New version * Mon Jul 25 2011 David Lutterkort - 0.9.0-1 - New version; removed patch pathx-whitespace-ea010d8 * Tue May 3 2011 David Lutterkort - 0.8.1-2 - Add patch pathx-whitespace-ea010d8.patch to fix BZ 700608 * Fri Apr 15 2011 David Lutterkort - 0.8.1-1 - New version * Wed Feb 23 2011 David Lutterkort - 0.8.0-1 - New version * Mon Feb 07 2011 Fedora Release Engineering - 0.7.4-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild * Mon Nov 22 2010 Matthew Booth - 0.7.4-1 - Update to version 0.7.4 * Thu Nov 18 2010 Richard W.M. Jones - 0.7.3-2 - Upstream patch proposed to fix GCC optimization bug (RHBZ#651992). * Fri Aug 6 2010 David Lutterkort - 0.7.3-1 - Remove upstream patches * Tue Jun 29 2010 David Lutterkort - 0.7.2-2 - Patches based on upstream fix for BZ 600141 * Tue Jun 22 2010 David Lutterkort - 0.7.2-1 - Fix ownership of /usr/share/augeas. BZ 569393 * Wed Apr 21 2010 David Lutterkort - 0.7.1-1 - New version * Thu Jan 14 2010 David Lutterkort - 0.7.0-1 - Remove patch vim-ftdetect-syntax.patch. It's upstream * Tue Dec 15 2009 David Lutterkort - 0.6.0-2 - Fix ftdetect file for vim * Mon Nov 30 2009 David Lutterkort - 0.6.0-1 - Install vim syntax files * Mon Sep 14 2009 David Lutterkort - 0.5.3-1 - Remove separate xorg.aug, included in upstream source * Tue Aug 25 2009 Matthew Booth - 0.5.2-3 - Include new xorg lens from upstream * Fri Jul 24 2009 Fedora Release Engineering - 0.5.2-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild * Mon Jul 13 2009 David Lutterkort - 0.5.2-1 - New version * Fri Jun 5 2009 David Lutterkort - 0.5.1-1 - Install fadot * Fri Mar 27 2009 David Lutterkort - 0.5.0-2 - fadot isn't being installed just yet * Tue Mar 24 2009 David Lutterkort - 0.5.0-1 - New program /usr/bin/fadot * Mon Mar 9 2009 David Lutterkort - 0.4.2-1 - New version * Fri Feb 27 2009 David Lutterkort - 0.4.1-1 - New version * Fri Feb 6 2009 David Lutterkort - 0.4.0-1 - New version * Mon Jan 26 2009 David Lutterkort - 0.3.6-1 - New version * Tue Dec 23 2008 David Lutterkort - 0.3.5-1 - New version * Mon Feb 25 2008 David Lutterkort - 0.0.4-1 - Initial specfile augeas-1.13.0/src/0000755000175000017500000000000014161102722010625 500000000000000augeas-1.13.0/src/augmatch.c0000644000175000017500000003373114161102026012506 00000000000000/* * augrp.c: utility for printing and reading files as parsed by Augeas * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include #include #include #include #include #include "memory.h" #include "augeas.h" #include #define EXIT_TROUBLE 2 #define cleanup(_x) __attribute__((__cleanup__(_x))) const char *progname; bool print_all = false; bool print_only_values = false; bool print_exact = false; static void freep(void *p) { free(*(void **)p); } static void aug_closep(struct augeas **p) { aug_close(*p); } __attribute__((noreturn)) static void usage(void) { fprintf(stderr, "Usage: %s [OPTIONS] FILE\n", progname); fprintf(stderr, "Print the contents of a file as parsed by augeas.\n\n" "Options:\n\n" " -l, --lens LENS use LENS to transform the file\n" " -L, --print-lens print the lens that will be used for a file an exit\n" " -a, --all print all nodes, even ones without a value\n" " -m, --match EXPR start printing where nodes match EXPR\n" " -e, --exact print only exact matches instead of the entire tree\n" " starting at a match\n" " -o, --only-value print only the values of tree nodes, but no path\n" " -q, --quiet do not print anything. Exit with zero status if a\n" " match was found\n" " -r, --root ROOT use ROOT as the root of the filesystem\n" " -I, --include DIR search DIR for modules; can be given mutiple times\n" " -S, --nostdinc do not search the builtin default directories\n" " for modules\n\n" "Examples:\n\n" " Print how augeas sees /etc/exports:\n" " augmatch /etc/exports\n\n" " Show only the entry for a specific mount:\n" " augmatch -m 'dir[\"/home\"]' /etc/exports\n\n" " Show all the clients to which we are exporting /home:\n" " augmatch -eom 'dir[\"/home\"]/client' /etc/exports\n\n"); exit(EXIT_SUCCESS); } /* Exit with a failure if COND is true. Use FORMAT and the remaining * arguments like printf to print an error message on stderr before * exiting */ static void die(bool cond, const char *format, ...) { if (cond) { fputs("error: ", stderr); va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args); exit(EXIT_TROUBLE); } } static void oom_when(bool cond) { die(cond, "out of memory.\n"); } /* Format a string with vasprintf and return it. The caller is responsible * for freeing the result. */ static char *format(const char *format, ...) { va_list args; char *result; int r; va_start(args, format); r = vasprintf(&result, format, args); va_end(args); oom_when(r < 0); return result; } /* Check for an Augeas error. If there is one, print it and all its detail * and exit with failure. If there is no error, do nothing */ static void check_error(struct augeas *aug) { die(aug == NULL, "could not initialize augeas\n"); oom_when(aug_error(aug) == AUG_ENOMEM); if (aug_error(aug) != AUG_NOERROR) { fprintf(stderr, "error: %s\n", aug_error_message(aug)); const char *msg = aug_error_minor_message(aug); if (msg != NULL) { fprintf(stderr, "%s\n", msg); } msg = aug_error_details(aug); if (msg != NULL) { fprintf(stderr, "%s\n", msg); } exit(EXIT_TROUBLE); } } /* Check for an error trying to load FILE (e.g., a parse error) If there * was one, print details and exit with failure. If there was none, do * nothing. */ static void check_load_error(struct augeas *aug, const char *file) { char *info = format("/augeas/files%s", file); const char *msg, *line, *col; aug_defvar(aug, "info", info); free(info); die(aug_ns_count(aug, "info") == 0, "file %s does not exist\n", file); aug_defvar(aug, "error", "$info/error"); if (aug_ns_count(aug, "error") == 0) return; aug_get(aug, "$error", &msg); aug_get(aug, "$error/line", &line); aug_get(aug, "$error/char", &col); if (streqv(msg, "parse_failed")) { msg = "parsing failed"; } else if (streqv(msg, "read_failed")) { aug_get(aug, "$error/message", &msg); } if ((line != NULL) && (col != NULL)) { fprintf(stderr, "error reading %s: %s on line %s, column %s\n", file, msg, line, col); } else { fprintf(stderr, "error reading %s: %s\n", file, msg); } exit(EXIT_TROUBLE); } /* We keep track of where we are in the tree when we are printing it by * assigning to augeas variables and using one struct node for each level * in the tree. To keep things simple, we just preallocate a lot of them * (up to max_nodes many). If we ever have a tree deeper than this, we are * in trouble and will simply abort the program. */ static const size_t max_nodes = 256; struct node { char *var; /* The variable where we store the nodes for this level */ const char *label; /* The label, index, and value of the current node */ int index; /* at the level that this struct node is for */ const char *value; }; /* Print information about NODES[LEVEL] by printing the path to it going * from NODES[0] to NODES[LEVEL]. PREFIX is the path from the start of the * file to the current match (if the user specified --match) or the empty * string (if we are printing the entire file. */ static void print_one(int level, const char *prefix, struct node *nodes) { if (nodes[level].value == NULL && ! print_all) return; if (print_only_values && nodes[level].value != NULL) { printf("%s\n", nodes[level].value); return; } if (*prefix) { if (level > 0) printf("%s/", prefix); else printf("%s", prefix); } for (int i=1; i <= level; i++) { if (nodes[i].index > 0) { printf("%s[%d]", nodes[i].label, nodes[i].index); } else { printf("%s", nodes[i].label); } if (i < level) { printf("/"); } } if (nodes[level].value) { printf(" = %s\n", nodes[level].value); } else { printf("\n"); } } /* Recursively print the tree starting at NODES[LEVEL] */ static void print_tree(struct augeas *aug, int level, const char *prefix, struct node *nodes) { die(level + 1 >= max_nodes, "tree has more than %d levels, which is more than we can handle\n", max_nodes); struct node *cur = nodes + level; struct node *next = cur + 1; int count = aug_ns_count(aug, cur->var); for (int i=0; i < count; i++) { cleanup(freep) char *pattern = NULL; aug_ns_label(aug, cur->var, i, &(cur->label), &(cur->index)); aug_ns_value(aug, cur->var, i, &(cur->value)); print_one(level, prefix, nodes); if (! print_exact) { pattern = format("$%s[%d]/*", cur->var, i+1); aug_defvar(aug, next->var, pattern); check_error(aug); print_tree(aug, level+1, prefix, nodes); } } } /* Print the tree for file PATH (which must already start with /files), but * only the nodes matching MATCH. * * Return EXIT_SUCCESS if there was at least one match, and EXIT_FAILURE * if there was none. */ static int print(struct augeas *aug, const char *path, const char *match) { static const char *const match_var = "match"; struct node *nodes = NULL; nodes = calloc(max_nodes, sizeof(struct node)); oom_when(nodes == NULL); for (int i=0; i < max_nodes; i++) { nodes[i].var = format("var%d", i); } /* Set $match to the nodes matching the user's match expression */ aug_defvar(aug, match_var, match); check_error(aug); /* Go through the matches in MATCH_VAR one by one. We need to do it * this way, since the prefix we need to print for each entry in * MATCH_VAR is different for each entry. */ int count = aug_ns_count(aug, match_var); for (int i=0; i < count; i++) { cleanup(freep) char *prefix = NULL; aug_ns_path(aug, match_var, i, &prefix); aug_defvar(aug, nodes[0].var, prefix); print_tree(aug, 0, prefix + strlen(path) + 1, nodes); } for (int i=0; i < max_nodes; i++) { free(nodes[i].var); } free(nodes); return (count == 0) ? EXIT_FAILURE : EXIT_SUCCESS; } /* Look at the filename and try to guess based on the extension. The * builtin filters for lenses do not do that, as that would force augtool * to scan everything on start */ static char *guess_lens_name(const char *file) { const char *ext = strrchr(file, '.'); if (ext == NULL) return NULL; if (streqv(ext, ".json")) { return strdup("Json.lns"); } else if (streqv(ext, ".xml")) { return strdup("Xml.lns"); } return NULL; } int main(int argc, char **argv) { int opt; cleanup(aug_closep) struct augeas *aug; cleanup(freep) char *loadpath = NULL; size_t loadpath_len = 0; cleanup(freep) char *root = NULL; cleanup(freep) char *lens = NULL; cleanup(freep) char *matches = NULL; size_t matches_len = 0; const char *match = "*"; bool print_lens = false; bool quiet = false; int result = EXIT_SUCCESS; struct option options[] = { { "help", 0, 0, 'h' }, { "include", 1, 0, 'I' }, { "lens", 1, 0, 'l' }, { "all", 0, 0, 'a' }, { "index", 0, 0, 'i' }, { "match", 1, 0, 'm' }, { "only-value", 0, 0, 'o' }, { "nostdinc", 0, 0, 'S' }, { "root", 1, 0, 'r' }, { "print-lens", 0, 0, 'L' }, { "exact", 0, 0, 'e' }, { "quiet", 0, 0, 'q' }, { 0, 0, 0, 0} }; unsigned int flags = AUG_NO_LOAD|AUG_NO_ERR_CLOSE; progname = basename(argv[0]); setlocale(LC_ALL, ""); while ((opt = getopt_long(argc, argv, "ahI:l:m:oSr:eLq", options, NULL)) != -1) { switch(opt) { case 'I': argz_add(&loadpath, &loadpath_len, optarg); break; case 'l': lens = strdup(optarg); break; case 'L': print_lens = true; break; case 'h': usage(); break; case 'a': print_all = true; break; case 'm': // If optarg is a numeric string like '1', it is not a legal // part of a path by itself, and so we need to prefix it with // an explicit axis die(optarg[0] == '/', "matches can only be relative paths, not %s\n", optarg); argz_add(&matches, &matches_len, format("child::%s", optarg)); break; case 'o': print_only_values = true; break; case 'r': root = strdup(optarg); break; case 'S': flags |= AUG_NO_STDINC; break; case 'e': print_exact = true; break; case 'q': quiet = true; break; default: fprintf(stderr, "Try '%s --help' for more information.\n", progname); exit(EXIT_TROUBLE); break; } } if (optind >= argc) { fprintf(stderr, "Expected an input file\n"); fprintf(stderr, "Try '%s --help' for more information.\n", progname); exit(EXIT_TROUBLE); } const char *file = argv[optind]; argz_stringify(loadpath, loadpath_len, ':'); if (lens == NULL) { lens = guess_lens_name(file); } if (lens != NULL) { /* We know which lens we want, we do not need to load all of them */ flags |= AUG_NO_MODL_AUTOLOAD; } aug = aug_init(root, loadpath, flags|AUG_NO_ERR_CLOSE); check_error(aug); if (lens == NULL) { aug_load_file(aug, file); } else { aug_transform(aug, lens, file, false); aug_load(aug); } check_error(aug); /* The user just wants the lens name */ if (print_lens) { char *info = format("/augeas/files%s", file); const char *lens_name; aug_defvar(aug, "info", info); die(aug_ns_count(aug, "info") == 0, "file %s does not exist\n", file); aug_get(aug, "$info/lens", &lens_name); /* We are being extra careful here - the check_error above would have already aborted the program if we could not determine a lens; dieing here indicates some sort of bug */ die(lens_name == NULL, "could not find lens for %s\n", file); if (lens_name[0] == '@') lens_name += 1; printf("%s\n", lens_name); exit(EXIT_SUCCESS); } check_load_error(aug, file); char *path = format("/files%s", file); aug_set(aug, "/augeas/context", path); if (matches_len > 0) { argz_stringify(matches, matches_len, '|'); match = matches; } if (quiet) { int n = aug_match(aug, match, NULL); check_error(aug); result = (n == 0) ? EXIT_FAILURE : EXIT_SUCCESS; } else { result = print(aug, path, match); } free(path); return result; } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/augeas_sym.version0000644000175000017500000000271014161102026014306 00000000000000AUGEAS_0.1.0 { global: aug_init; aug_close; aug_get; aug_set; aug_insert; aug_rm; aug_mv; aug_match; aug_save; aug_print; # Symbols with __ are private __aug_load_module_file; local: *; }; AUGEAS_0.8.0 { global: aug_defvar; aug_defnode; aug_load; } AUGEAS_0.1.0; AUGEAS_0.10.0 { global: aug_error; aug_error_message; aug_error_minor_message; aug_error_details; } AUGEAS_0.8.0; AUGEAS_0.11.0 { global: aug_setm; } AUGEAS_0.10.0; AUGEAS_0.12.0 { global: aug_span; } AUGEAS_0.11.0; AUGEAS_0.14.0 { global: aug_srun; __aug_init_memstream; __aug_close_memstream; } AUGEAS_0.12.0; AUGEAS_0.15.0 { global: aug_to_xml; } AUGEAS_0.14.0; AUGEAS_0.16.0 { global: aug_text_store; aug_text_retrieve; aug_rename; aug_transform; aug_label; } AUGEAS_0.15.0; AUGEAS_0.18.0 { global: aug_cp; } AUGEAS_0.16.0; AUGEAS_0.20.0 { global: aug_escape_name; } AUGEAS_0.18.0; AUGEAS_0.21.0 { global: aug_load_file; } AUGEAS_0.20.0; AUGEAS_0.22.0 { global: aug_source; } AUGEAS_0.21.0; AUGEAS_0.23.0 { global: aug_ns_attr; } AUGEAS_0.22.0; AUGEAS_0.24.0 { global: aug_ns_label; aug_ns_value; aug_ns_count; aug_ns_path; } AUGEAS_0.23.0; AUGEAS_0.25.0 { global: aug_preview; } AUGEAS_0.24.0; augeas-1.13.0/src/parser.c0000644000175000017500000026152714161102722012222 00000000000000/* A Bison parser, made by GNU Bison 3.8.2. */ /* Bison implementation for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, especially those whose name start with YY_ or yy_. They are private implementation details that can be changed or removed. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output, and Bison version. */ #define YYBISON 30802 /* Bison version string. */ #define YYBISON_VERSION "3.8.2" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 1 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Substitute the variable and function names. */ #define yyparse augl_parse #define yylex augl_lex #define yyerror augl_error #define yydebug augl_debug #define yynerrs augl_nerrs /* First part of user prologue. */ #line 1 "parser.y" #include #include "internal.h" #include "syntax.h" #include "list.h" #include "errcode.h" #include /* Work around a problem on FreeBSD where Bison looks for _STDLIB_H * to see if stdlib.h has been included, but the system includes * use _STDLIB_H_ */ #if HAVE_STDLIB_H && ! defined _STDLIB_H # include # define _STDLIB_H 1 #endif #define YYDEBUG 1 int augl_parse_file(struct augeas *aug, const char *name, struct term **term); typedef void *yyscan_t; typedef struct info YYLTYPE; #define YYLTYPE_IS_DECLARED 1 /* The lack of reference counting on filename is intentional */ # define YYLLOC_DEFAULT(Current, Rhs, N) \ do { \ (Current).filename = augl_get_info(scanner)->filename; \ (Current).error = augl_get_info(scanner)->error; \ if (N) { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } else { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ } while (0) #line 121 "parser.c" # ifndef YY_CAST # ifdef __cplusplus # define YY_CAST(Type, Val) static_cast (Val) # define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) # else # define YY_CAST(Type, Val) ((Type) (Val)) # define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) # endif # endif # ifndef YY_NULLPTR # if defined __cplusplus # if 201103L <= __cplusplus # define YY_NULLPTR nullptr # else # define YY_NULLPTR 0 # endif # else # define YY_NULLPTR ((void*)0) # endif # endif /* Use api.header.include to #include this header instead of duplicating it here. */ #ifndef YY_AUGL_PARSER_H_INCLUDED # define YY_AUGL_PARSER_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int augl_debug; #endif /* Token kinds. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { YYEMPTY = -2, YYEOF = 0, /* "end of file" */ YYerror = 256, /* error */ YYUNDEF = 257, /* "invalid token" */ DQUOTED = 258, /* DQUOTED */ REGEXP = 259, /* REGEXP */ LIDENT = 260, /* LIDENT */ UIDENT = 261, /* UIDENT */ QIDENT = 262, /* QIDENT */ ARROW = 263, /* ARROW */ KW_MODULE = 264, /* KW_MODULE */ KW_AUTOLOAD = 265, /* KW_AUTOLOAD */ KW_LET = 266, /* KW_LET */ KW_LET_REC = 267, /* KW_LET_REC */ KW_IN = 268, /* KW_IN */ KW_STRING = 269, /* KW_STRING */ KW_REGEXP = 270, /* KW_REGEXP */ KW_LENS = 271, /* KW_LENS */ KW_TEST = 272, /* KW_TEST */ KW_GET = 273, /* KW_GET */ KW_PUT = 274, /* KW_PUT */ KW_AFTER = 275 /* KW_AFTER */ }; typedef enum yytokentype yytoken_kind_t; #endif /* Token kinds. */ #define YYEMPTY -2 #define YYEOF 0 #define YYerror 256 #define YYUNDEF 257 #define DQUOTED 258 #define REGEXP 259 #define LIDENT 260 #define UIDENT 261 #define QIDENT 262 #define ARROW 263 #define KW_MODULE 264 #define KW_AUTOLOAD 265 #define KW_LET 266 #define KW_LET_REC 267 #define KW_IN 268 #define KW_STRING 269 #define KW_REGEXP 270 #define KW_LENS 271 #define KW_TEST 272 #define KW_GET 273 #define KW_PUT 274 #define KW_AFTER 275 /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 89 "parser.y" struct term *term; struct type *type; struct ident *ident; struct tree *tree; char *string; struct { int nocase; char *pattern; } regexp; int intval; enum quant_tag quant; #line 228 "parser.c" }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif /* Location type. */ #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE YYLTYPE; struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; }; # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif int augl_parse (struct term **term, yyscan_t scanner); /* "%code provides" blocks. */ #line 46 "parser.y" #include "info.h" /* Track custom scanner state */ struct state { struct info *info; unsigned int comment_depth; }; #line 267 "parser.c" #endif /* !YY_AUGL_PARSER_H_INCLUDED */ /* Symbol kind. */ enum yysymbol_kind_t { YYSYMBOL_YYEMPTY = -2, YYSYMBOL_YYEOF = 0, /* "end of file" */ YYSYMBOL_YYerror = 1, /* error */ YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ YYSYMBOL_DQUOTED = 3, /* DQUOTED */ YYSYMBOL_REGEXP = 4, /* REGEXP */ YYSYMBOL_LIDENT = 5, /* LIDENT */ YYSYMBOL_UIDENT = 6, /* UIDENT */ YYSYMBOL_QIDENT = 7, /* QIDENT */ YYSYMBOL_ARROW = 8, /* ARROW */ YYSYMBOL_KW_MODULE = 9, /* KW_MODULE */ YYSYMBOL_KW_AUTOLOAD = 10, /* KW_AUTOLOAD */ YYSYMBOL_KW_LET = 11, /* KW_LET */ YYSYMBOL_KW_LET_REC = 12, /* KW_LET_REC */ YYSYMBOL_KW_IN = 13, /* KW_IN */ YYSYMBOL_KW_STRING = 14, /* KW_STRING */ YYSYMBOL_KW_REGEXP = 15, /* KW_REGEXP */ YYSYMBOL_KW_LENS = 16, /* KW_LENS */ YYSYMBOL_KW_TEST = 17, /* KW_TEST */ YYSYMBOL_KW_GET = 18, /* KW_GET */ YYSYMBOL_KW_PUT = 19, /* KW_PUT */ YYSYMBOL_KW_AFTER = 20, /* KW_AFTER */ YYSYMBOL_21_ = 21, /* '=' */ YYSYMBOL_22_ = 22, /* '?' */ YYSYMBOL_23_ = 23, /* '*' */ YYSYMBOL_24_ = 24, /* ';' */ YYSYMBOL_25_ = 25, /* '|' */ YYSYMBOL_26_ = 26, /* '-' */ YYSYMBOL_27_ = 27, /* '.' */ YYSYMBOL_28_ = 28, /* '(' */ YYSYMBOL_29_ = 29, /* ')' */ YYSYMBOL_30_ = 30, /* '[' */ YYSYMBOL_31_ = 31, /* ']' */ YYSYMBOL_32_ = 32, /* '+' */ YYSYMBOL_33_ = 33, /* ':' */ YYSYMBOL_34_ = 34, /* '{' */ YYSYMBOL_35_ = 35, /* '}' */ YYSYMBOL_YYACCEPT = 36, /* $accept */ YYSYMBOL_start = 37, /* start */ YYSYMBOL_autoload = 38, /* autoload */ YYSYMBOL_decls = 39, /* decls */ YYSYMBOL_test_exp = 40, /* test_exp */ YYSYMBOL_test_special_res = 41, /* test_special_res */ YYSYMBOL_exp = 42, /* exp */ YYSYMBOL_composeexp = 43, /* composeexp */ YYSYMBOL_unionexp = 44, /* unionexp */ YYSYMBOL_minusexp = 45, /* minusexp */ YYSYMBOL_catexp = 46, /* catexp */ YYSYMBOL_appexp = 47, /* appexp */ YYSYMBOL_aexp = 48, /* aexp */ YYSYMBOL_rexp = 49, /* rexp */ YYSYMBOL_rep = 50, /* rep */ YYSYMBOL_qid = 51, /* qid */ YYSYMBOL_param_list = 52, /* param_list */ YYSYMBOL_param = 53, /* param */ YYSYMBOL_id = 54, /* id */ YYSYMBOL_type = 55, /* type */ YYSYMBOL_atype = 56, /* atype */ YYSYMBOL_tree_const = 57, /* tree_const */ YYSYMBOL_tree_const2 = 58, /* tree_const2 */ YYSYMBOL_tree_branch = 59, /* tree_branch */ YYSYMBOL_tree_label = 60 /* tree_label */ }; typedef enum yysymbol_kind_t yysymbol_kind_t; /* Second part of user prologue. */ #line 114 "parser.y" /* Lexer */ extern int augl_lex (YYSTYPE * yylval_param,struct info * yylloc_param ,yyscan_t yyscanner); int augl_init_lexer(struct state *state, yyscan_t * scanner); void augl_close_lexer(yyscan_t *scanner); int augl_lex_destroy (yyscan_t yyscanner ); int augl_get_lineno (yyscan_t yyscanner ); int augl_get_column (yyscan_t yyscanner); struct info *augl_get_info(yyscan_t yyscanner); char *augl_get_text (yyscan_t yyscanner ); static void augl_error(struct info *locp, struct term **term, yyscan_t scanner, const char *s); /* TERM construction */ static struct info *clone_info(struct info *locp); static struct term *make_module(char *ident, char *autoload, struct term *decls, struct info *locp); static struct term *make_bind(char *ident, struct term *params, struct term *exp, struct term *decls, struct info *locp); static struct term *make_bind_rec(char *ident, struct term *exp, struct term *decls, struct info *locp); static struct term *make_let(char *ident, struct term *params, struct term *exp, struct term *body, struct info *locp); static struct term *make_binop(enum term_tag tag, struct term *left, struct term *right, struct info *locp); static struct term *make_unop(enum term_tag tag, struct term *exp, struct info *locp); static struct term *make_ident(char *qname, struct info *locp); static struct term *make_unit_term(struct info *locp); static struct term *make_string_term(char *value, struct info *locp); static struct term *make_regexp_term(char *pattern, int nocase, struct info *locp); static struct term *make_rep(struct term *exp, enum quant_tag quant, struct info *locp); static struct term *make_get_test(struct term *lens, struct term *arg, struct info *info); static struct term *make_put_test(struct term *lens, struct term *arg, struct term *cmds, struct info *info); static struct term *make_test(struct term *test, struct term *result, enum test_result_tag tr_tag, struct term *decls, struct info *locp); static struct term *make_tree_value(struct tree *, struct info*); static struct tree *tree_concat(struct tree *, struct tree *); #define LOC_MERGE(a, b, c) \ do { \ (a).filename = (b).filename; \ (a).first_line = (b).first_line; \ (a).first_column = (b).first_column; \ (a).last_line = (c).last_line; \ (a).last_column = (c).last_column; \ (a).error = (b).error; \ } while(0); #line 403 "parser.c" #ifdef short # undef short #endif /* On compilers that do not define __PTRDIFF_MAX__ etc., make sure and (if available) are included so that the code can choose integer types of a good width. */ #ifndef __PTRDIFF_MAX__ # include /* INFRINGES ON USER NAME SPACE */ # if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ # include /* INFRINGES ON USER NAME SPACE */ # define YY_STDINT_H # endif #endif /* Narrow types that promote to a signed type and that can represent a signed or unsigned integer of at least N bits. In tables they can save space and decrease cache pressure. Promoting to a signed type helps avoid bugs in integer arithmetic. */ #ifdef __INT_LEAST8_MAX__ typedef __INT_LEAST8_TYPE__ yytype_int8; #elif defined YY_STDINT_H typedef int_least8_t yytype_int8; #else typedef signed char yytype_int8; #endif #ifdef __INT_LEAST16_MAX__ typedef __INT_LEAST16_TYPE__ yytype_int16; #elif defined YY_STDINT_H typedef int_least16_t yytype_int16; #else typedef short yytype_int16; #endif /* Work around bug in HP-UX 11.23, which defines these macros incorrectly for preprocessor constants. This workaround can likely be removed in 2023, as HPE has promised support for HP-UX 11.23 (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of . */ #ifdef __hpux # undef UINT_LEAST8_MAX # undef UINT_LEAST16_MAX # define UINT_LEAST8_MAX 255 # define UINT_LEAST16_MAX 65535 #endif #if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ typedef __UINT_LEAST8_TYPE__ yytype_uint8; #elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ && UINT_LEAST8_MAX <= INT_MAX) typedef uint_least8_t yytype_uint8; #elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX typedef unsigned char yytype_uint8; #else typedef short yytype_uint8; #endif #if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ typedef __UINT_LEAST16_TYPE__ yytype_uint16; #elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ && UINT_LEAST16_MAX <= INT_MAX) typedef uint_least16_t yytype_uint16; #elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX typedef unsigned short yytype_uint16; #else typedef int yytype_uint16; #endif #ifndef YYPTRDIFF_T # if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ # define YYPTRDIFF_T __PTRDIFF_TYPE__ # define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ # elif defined PTRDIFF_MAX # ifndef ptrdiff_t # include /* INFRINGES ON USER NAME SPACE */ # endif # define YYPTRDIFF_T ptrdiff_t # define YYPTRDIFF_MAXIMUM PTRDIFF_MAX # else # define YYPTRDIFF_T long # define YYPTRDIFF_MAXIMUM LONG_MAX # endif #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned # endif #endif #define YYSIZE_MAXIMUM \ YY_CAST (YYPTRDIFF_T, \ (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ ? YYPTRDIFF_MAXIMUM \ : YY_CAST (YYSIZE_T, -1))) #define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) /* Stored state numbers (used for stacks). */ typedef yytype_int8 yy_state_t; /* State numbers in computations. */ typedef int yy_state_fast_t; #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ # define YY_(Msgid) Msgid # endif #endif #ifndef YY_ATTRIBUTE_PURE # if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) # define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) # else # define YY_ATTRIBUTE_PURE # endif #endif #ifndef YY_ATTRIBUTE_UNUSED # if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) # define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) # else # define YY_ATTRIBUTE_UNUSED # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YY_USE(E) ((void) (E)) #else # define YY_USE(E) /* empty */ #endif /* Suppress an incorrect diagnostic about yylval being uninitialized. */ #if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ # if __GNUC__ * 100 + __GNUC_MINOR__ < 407 # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") # else # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") # endif # define YY_IGNORE_MAYBE_UNINITIALIZED_END \ _Pragma ("GCC diagnostic pop") #else # define YY_INITIAL_VALUE(Value) Value #endif #ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif #ifndef YY_INITIAL_VALUE # define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif #if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ # define YY_IGNORE_USELESS_CAST_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") # define YY_IGNORE_USELESS_CAST_END \ _Pragma ("GCC diagnostic pop") #endif #ifndef YY_IGNORE_USELESS_CAST_BEGIN # define YY_IGNORE_USELESS_CAST_BEGIN # define YY_IGNORE_USELESS_CAST_END #endif #define YY_ASSERT(E) ((void) (0 && (E))) #if 1 /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS # include /* INFRINGES ON USER NAME SPACE */ /* Use EXIT_SUCCESS as a witness for stdlib.h. */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's 'empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined EXIT_SUCCESS void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined EXIT_SUCCESS void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* 1 */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yy_state_t yyss_alloc; YYSTYPE yyvs_alloc; YYLTYPE yyls_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + YYSIZEOF (YYLTYPE)) \ + 2 * YYSTACK_GAP_MAXIMUM) # define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYPTRDIFF_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / YYSIZEOF (*yyptr); \ } \ while (0) #endif #if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from SRC to DST. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(Dst, Src, Count) \ __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) # else # define YYCOPY(Dst, Src, Count) \ do \ { \ YYPTRDIFF_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (Dst)[yyi] = (Src)[yyi]; \ } \ while (0) # endif # endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 4 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 129 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 36 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 25 /* YYNRULES -- Number of rules. */ #define YYNRULES 61 /* YYNSTATES -- Number of states. */ #define YYNSTATES 113 /* YYMAXUTOK -- Last valid token kind. */ #define YYMAXUTOK 275 /* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM as returned by yylex, with out-of-bounds checking. */ #define YYTRANSLATE(YYX) \ (0 <= (YYX) && (YYX) <= YYMAXUTOK \ ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ : YYSYMBOL_YYUNDEF) /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM as returned by yylex. */ static const yytype_int8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 28, 29, 23, 32, 2, 26, 27, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 33, 24, 2, 21, 2, 22, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 30, 2, 31, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 34, 25, 35, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_int16 yyrline[] = { 0, 179, 179, 182, 185, 187, 192, 197, 202, 208, 212, 214, 217, 219, 223, 228, 230, 232, 235, 237, 239, 242, 244, 247, 249, 252, 254, 257, 259, 261, 263, 265, 267, 270, 272, 275, 277, 279, 282, 284, 286, 288, 291, 294, 296, 299, 301, 303, 306, 308, 311, 313, 315, 317, 320, 322, 325, 330, 332, 336, 340, 342 }; #endif /** Accessing symbol of state STATE. */ #define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) #if 1 /* The user-facing name of the symbol whose (internal) number is YYSYMBOL. No bounds checking. */ static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "\"end of file\"", "error", "\"invalid token\"", "DQUOTED", "REGEXP", "LIDENT", "UIDENT", "QIDENT", "ARROW", "KW_MODULE", "KW_AUTOLOAD", "KW_LET", "KW_LET_REC", "KW_IN", "KW_STRING", "KW_REGEXP", "KW_LENS", "KW_TEST", "KW_GET", "KW_PUT", "KW_AFTER", "'='", "'?'", "'*'", "';'", "'|'", "'-'", "'.'", "'('", "')'", "'['", "']'", "'+'", "':'", "'{'", "'}'", "$accept", "start", "autoload", "decls", "test_exp", "test_special_res", "exp", "composeexp", "unionexp", "minusexp", "catexp", "appexp", "aexp", "rexp", "rep", "qid", "param_list", "param", "id", "type", "atype", "tree_const", "tree_const2", "tree_branch", "tree_label", YY_NULLPTR }; static const char * yysymbol_name (yysymbol_kind_t yysymbol) { return yytname[yysymbol]; } #endif #define YYPACT_NINF (-90) #define yypact_value_is_default(Yyn) \ ((Yyn) == YYPACT_NINF) #define YYTABLE_NINF (-1) #define yytable_value_is_error(Yyn) \ 0 /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ static const yytype_int8 yypact[] = { -1, 8, 16, -4, -90, 14, 26, 53, -90, 27, 29, 92, -90, 11, 25, -90, -90, -90, -90, -90, -90, 50, 55, 28, -6, -90, 1, 30, 11, 55, 38, -90, 44, 34, 52, 57, 51, 59, 92, 49, -90, 64, 56, 22, 55, 92, -90, -90, -90, 60, 55, -90, 53, 11, -90, 65, 80, -90, 87, 92, 92, 92, -90, -90, -90, -90, -90, 44, -90, -90, -90, 53, 53, -90, 82, -5, 53, -90, 83, -90, 100, 73, 57, 51, 59, 92, 74, -90, -90, 55, -90, -90, -90, -5, 79, 104, -90, 55, -90, 44, -90, -90, 84, -90, -5, 101, 73, 81, -90, -90, 55, -90, -90 }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. Performed when YYTABLE does not specify something else to do. Zero means the default is an error. */ static const yytype_int8 yydefact[] = { 0, 0, 0, 0, 1, 4, 0, 9, 3, 0, 0, 0, 2, 43, 0, 28, 29, 38, 39, 40, 41, 0, 0, 0, 0, 27, 0, 0, 43, 0, 0, 32, 61, 0, 15, 17, 19, 22, 24, 34, 26, 20, 0, 0, 0, 0, 45, 46, 47, 0, 0, 42, 9, 43, 60, 0, 57, 30, 0, 0, 0, 0, 25, 37, 35, 36, 33, 61, 31, 12, 13, 9, 9, 10, 0, 0, 9, 6, 0, 55, 0, 58, 16, 18, 21, 23, 0, 8, 7, 0, 50, 51, 52, 0, 0, 49, 5, 0, 57, 61, 54, 11, 0, 44, 0, 0, 59, 0, 53, 48, 0, 56, 14 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { -90, -90, -90, -34, -90, -90, -22, -90, 61, 66, 58, 62, -9, -37, -90, -90, -23, -90, -90, -89, -90, -90, 31, -64, -90 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { 0, 2, 7, 12, 23, 71, 33, 34, 35, 36, 37, 38, 39, 40, 66, 25, 27, 28, 49, 94, 95, 41, 81, 55, 56 }; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule whose number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_int8 yytable[] = { 42, 62, 24, 86, 102, 51, 46, 52, 1, 90, 91, 92, 44, 45, 3, 109, 4, 5, 77, 47, 48, 72, 73, 93, 6, 15, 16, 17, 76, 18, 78, 8, 13, 30, 14, 107, 74, 87, 88, 26, 19, 20, 96, 53, 69, 70, 29, 54, 62, 43, 21, 50, 22, 15, 16, 17, 32, 18, 15, 16, 17, 30, 18, 57, 9, 10, 30, 101, 19, 20, 11, 63, 64, 19, 20, 105, 58, 60, 21, 31, 22, 65, 59, 21, 32, 22, 61, 68, 112, 32, 15, 16, 17, 75, 18, 15, 16, 17, 67, 18, 79, 80, 89, 98, 97, 19, 20, 99, 103, 100, 19, 20, 104, 108, 110, 21, 111, 22, 84, 82, 21, 32, 22, 85, 0, 83, 0, 0, 0, 106 }; static const yytype_int8 yycheck[] = { 22, 38, 11, 67, 93, 28, 5, 29, 9, 14, 15, 16, 18, 19, 6, 104, 0, 21, 52, 18, 19, 43, 44, 28, 10, 3, 4, 5, 50, 7, 53, 5, 5, 11, 5, 99, 45, 71, 72, 28, 18, 19, 76, 5, 22, 23, 21, 3, 85, 21, 28, 21, 30, 3, 4, 5, 34, 7, 3, 4, 5, 11, 7, 29, 11, 12, 11, 89, 18, 19, 17, 22, 23, 18, 19, 97, 24, 26, 28, 29, 30, 32, 25, 28, 34, 30, 27, 31, 110, 34, 3, 4, 5, 33, 7, 3, 4, 5, 34, 7, 35, 21, 20, 3, 21, 18, 19, 34, 29, 35, 18, 19, 8, 29, 13, 28, 35, 30, 60, 58, 28, 34, 30, 61, -1, 59, -1, -1, -1, 98 }; /* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of state STATE-NUM. */ static const yytype_int8 yystos[] = { 0, 9, 37, 6, 0, 21, 10, 38, 5, 11, 12, 17, 39, 5, 5, 3, 4, 5, 7, 18, 19, 28, 30, 40, 48, 51, 28, 52, 53, 21, 11, 29, 34, 42, 43, 44, 45, 46, 47, 48, 49, 57, 42, 21, 18, 19, 5, 18, 19, 54, 21, 52, 42, 5, 3, 59, 60, 29, 24, 25, 26, 27, 49, 22, 23, 32, 50, 34, 31, 22, 23, 41, 42, 42, 48, 33, 42, 39, 52, 35, 21, 58, 44, 45, 46, 47, 59, 39, 39, 20, 14, 15, 16, 28, 55, 56, 39, 21, 3, 34, 35, 42, 55, 29, 8, 42, 58, 59, 29, 55, 13, 35, 42 }; /* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ static const yytype_int8 yyr1[] = { 0, 36, 37, 38, 38, 39, 39, 39, 39, 39, 40, 40, 41, 41, 42, 42, 43, 43, 44, 44, 44, 45, 45, 46, 46, 47, 47, 48, 48, 48, 48, 48, 48, 49, 49, 50, 50, 50, 51, 51, 51, 51, 52, 52, 53, 54, 54, 54, 55, 55, 56, 56, 56, 56, 57, 57, 58, 58, 59, 59, 60, 60 }; /* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ static const yytype_int8 yyr2[] = { 0, 2, 5, 2, 0, 6, 5, 5, 5, 0, 3, 5, 1, 1, 7, 1, 3, 1, 3, 1, 1, 3, 1, 3, 1, 2, 1, 1, 1, 1, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 5, 1, 1, 1, 3, 1, 1, 1, 1, 3, 4, 3, 4, 0, 2, 4, 1, 0 }; enum { YYENOMEM = -2 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab #define YYNOMEM goto yyexhaustedlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY) \ { \ yychar = (Token); \ yylval = (Value); \ YYPOPSTACK (yylen); \ yystate = *yyssp; \ goto yybackup; \ } \ else \ { \ yyerror (&yylloc, term, scanner, YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (0) /* Backward compatibility with an undocumented macro. Use YYerror or YYUNDEF. */ #define YYERRCODE YYUNDEF /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (N) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (0) #endif #define YYRHSLOC(Rhs, K) ((Rhs)[K]) /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (0) /* YYLOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ # ifndef YYLOCATION_PRINT # if defined YY_LOCATION_PRINT /* Temporary convenience wrapper in case some people defined the undocumented and private YY_LOCATION_PRINT macros. */ # define YYLOCATION_PRINT(File, Loc) YY_LOCATION_PRINT(File, *(Loc)) # elif defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL /* Print *YYLOCP on YYO. Private, do not rely on its existence. */ YY_ATTRIBUTE_UNUSED static int yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) { int res = 0; int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; if (0 <= yylocp->first_line) { res += YYFPRINTF (yyo, "%d", yylocp->first_line); if (0 <= yylocp->first_column) res += YYFPRINTF (yyo, ".%d", yylocp->first_column); } if (0 <= yylocp->last_line) { if (yylocp->first_line < yylocp->last_line) { res += YYFPRINTF (yyo, "-%d", yylocp->last_line); if (0 <= end_col) res += YYFPRINTF (yyo, ".%d", end_col); } else if (0 <= end_col && yylocp->first_column < end_col) res += YYFPRINTF (yyo, "-%d", end_col); } return res; } # define YYLOCATION_PRINT yy_location_print_ /* Temporary convenience wrapper in case some people defined the undocumented and private YY_LOCATION_PRINT macros. */ # define YY_LOCATION_PRINT(File, Loc) YYLOCATION_PRINT(File, &(Loc)) # else # define YYLOCATION_PRINT(File, Loc) ((void) 0) /* Temporary convenience wrapper in case some people defined the undocumented and private YY_LOCATION_PRINT macros. */ # define YY_LOCATION_PRINT YYLOCATION_PRINT # endif # endif /* !defined YYLOCATION_PRINT */ # define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Kind, Value, Location, term, scanner); \ YYFPRINTF (stderr, "\n"); \ } \ } while (0) /*-----------------------------------. | Print this symbol's value on YYO. | `-----------------------------------*/ static void yy_symbol_value_print (FILE *yyo, yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, struct term **term, yyscan_t scanner) { FILE *yyoutput = yyo; YY_USE (yyoutput); YY_USE (yylocationp); YY_USE (term); YY_USE (scanner); if (!yyvaluep) return; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN YY_USE (yykind); YY_IGNORE_MAYBE_UNINITIALIZED_END } /*---------------------------. | Print this symbol on YYO. | `---------------------------*/ static void yy_symbol_print (FILE *yyo, yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, struct term **term, yyscan_t scanner) { YYFPRINTF (yyo, "%s %s (", yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); YYLOCATION_PRINT (yyo, yylocationp); YYFPRINTF (yyo, ": "); yy_symbol_value_print (yyo, yykind, yyvaluep, yylocationp, term, scanner); YYFPRINTF (yyo, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ static void yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ static void yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, struct term **term, yyscan_t scanner) { int yylno = yyrline[yyrule]; int yynrhs = yyr2[yyrule]; int yyi; YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), &yyvsp[(yyi + 1) - (yynrhs)], &(yylsp[(yyi + 1) - (yynrhs)]), term, scanner); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyssp, yyvsp, yylsp, Rule, term, scanner); \ } while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) ((void) 0) # define YY_SYMBOL_PRINT(Title, Kind, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif /* Context of a parse error. */ typedef struct { yy_state_t *yyssp; yysymbol_kind_t yytoken; YYLTYPE *yylloc; } yypcontext_t; /* Put in YYARG at most YYARGN of the expected tokens given the current YYCTX, and return the number of tokens stored in YYARG. If YYARG is null, return the number of expected tokens (guaranteed to be less than YYNTOKENS). Return YYENOMEM on memory exhaustion. Return 0 if there are more than YYARGN expected tokens, yet fill YYARG up to YYARGN. */ static int yypcontext_expected_tokens (const yypcontext_t *yyctx, yysymbol_kind_t yyarg[], int yyargn) { /* Actual size of YYARG. */ int yycount = 0; int yyn = yypact[+*yyctx->yyssp]; if (!yypact_value_is_default (yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first -YYN actions for this state because they are default actions. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yyx; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYSYMBOL_YYerror && !yytable_value_is_error (yytable[yyx + yyn])) { if (!yyarg) ++yycount; else if (yycount == yyargn) return 0; else yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); } } if (yyarg && yycount == 0 && 0 < yyargn) yyarg[0] = YYSYMBOL_YYEMPTY; return yycount; } #ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) # else /* Return the length of YYSTR. */ static YYPTRDIFF_T yystrlen (const char *yystr) { YYPTRDIFF_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif #endif #ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ static char * yystpcpy (char *yydest, const char *yysrc) { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif #endif #ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYPTRDIFF_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYPTRDIFF_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; else goto append; append: default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (yyres) return yystpcpy (yyres, yystr) - yyres; else return yystrlen (yystr); } #endif static int yy_syntax_error_arguments (const yypcontext_t *yyctx, yysymbol_kind_t yyarg[], int yyargn) { /* Actual size of YYARG. */ int yycount = 0; /* There are many possibilities here to consider: - If this state is a consistent state with a default action, then the only way this function was invoked is if the default action is an error action. In that case, don't check for expected tokens because there are none. - The only way there can be no lookahead present (in yychar) is if this state is a consistent state with a default action. Thus, detecting the absence of a lookahead is sufficient to determine that there is no unexpected or expected token to report. In that case, just report a simple "syntax error". - Don't assume there isn't a lookahead just because this state is a consistent state with a default action. There might have been a previous inconsistent state, consistent state with a non-default action, or user semantic action that manipulated yychar. - Of course, the expected token list depends on states to have correct lookahead information, and it depends on the parser not to perform extra reductions after fetching a lookahead from the scanner and before detecting a syntax error. Thus, state merging (from LALR or IELR) and default reductions corrupt the expected token list. However, the list is correct for canonical LR with one exception: it will still contain any token that will not be accepted due to an error action in a later state. */ if (yyctx->yytoken != YYSYMBOL_YYEMPTY) { int yyn; if (yyarg) yyarg[yycount] = yyctx->yytoken; ++yycount; yyn = yypcontext_expected_tokens (yyctx, yyarg ? yyarg + 1 : yyarg, yyargn - 1); if (yyn == YYENOMEM) return YYENOMEM; else yycount += yyn; } return yycount; } /* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message about the unexpected token YYTOKEN for the state stack whose top is YYSSP. Return 0 if *YYMSG was successfully written. Return -1 if *YYMSG is not large enough to hold the message. In that case, also set *YYMSG_ALLOC to the required number of bytes. Return YYENOMEM if the required number of bytes is too large to store. */ static int yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, const yypcontext_t *yyctx) { enum { YYARGS_MAX = 5 }; /* Internationalized format string. */ const char *yyformat = YY_NULLPTR; /* Arguments of yyformat: reported tokens (one for the "unexpected", one per "expected"). */ yysymbol_kind_t yyarg[YYARGS_MAX]; /* Cumulated lengths of YYARG. */ YYPTRDIFF_T yysize = 0; /* Actual size of YYARG. */ int yycount = yy_syntax_error_arguments (yyctx, yyarg, YYARGS_MAX); if (yycount == YYENOMEM) return YYENOMEM; switch (yycount) { #define YYCASE_(N, S) \ case N: \ yyformat = S; \ break default: /* Avoid compiler warnings. */ YYCASE_(0, YY_("syntax error")); YYCASE_(1, YY_("syntax error, unexpected %s")); YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); #undef YYCASE_ } /* Compute error message size. Don't count the "%s"s, but reserve room for the terminator. */ yysize = yystrlen (yyformat) - 2 * yycount + 1; { int yyi; for (yyi = 0; yyi < yycount; ++yyi) { YYPTRDIFF_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyarg[yyi]]); if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) yysize = yysize1; else return YYENOMEM; } } if (*yymsg_alloc < yysize) { *yymsg_alloc = 2 * yysize; if (! (yysize <= *yymsg_alloc && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; return -1; } /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ { char *yyp = *yymsg; int yyi = 0; while ((*yyp = *yyformat) != '\0') if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yytname[yyarg[yyi++]]); yyformat += 2; } else { ++yyp; ++yyformat; } } return 0; } /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ static void yydestruct (const char *yymsg, yysymbol_kind_t yykind, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, struct term **term, yyscan_t scanner) { YY_USE (yyvaluep); YY_USE (yylocationp); YY_USE (term); YY_USE (scanner); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN YY_USE (yykind); YY_IGNORE_MAYBE_UNINITIALIZED_END } /*----------. | yyparse. | `----------*/ int yyparse (struct term **term, yyscan_t scanner) { /* Lookahead token kind. */ int yychar; /* The semantic value of the lookahead symbol. */ /* Default value used for initialization, for pacifying older GCCs or non-GCC compilers. */ YY_INITIAL_VALUE (static YYSTYPE yyval_default;) YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); /* Location data for the lookahead symbol. */ static YYLTYPE yyloc_default # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL = { 1, 1, 1, 1 } # endif ; YYLTYPE yylloc = yyloc_default; /* Number of syntax errors so far. */ int yynerrs = 0; yy_state_fast_t yystate = 0; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus = 0; /* Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* Their size. */ YYPTRDIFF_T yystacksize = YYINITDEPTH; /* The state stack: array, bottom, top. */ yy_state_t yyssa[YYINITDEPTH]; yy_state_t *yyss = yyssa; yy_state_t *yyssp = yyss; /* The semantic value stack: array, bottom, top. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs = yyvsa; YYSTYPE *yyvsp = yyvs; /* The location stack: array, bottom, top. */ YYLTYPE yylsa[YYINITDEPTH]; YYLTYPE *yyls = yylsa; YYLTYPE *yylsp = yyls; int yyn; /* The return value of yyparse. */ int yyresult; /* Lookahead symbol kind. */ yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; YYLTYPE yyloc; /* The locations where the error started and ended. */ YYLTYPE yyerror_range[3]; /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; YYDPRINTF ((stderr, "Starting parse\n")); yychar = YYEMPTY; /* Cause a token to be read. */ /* User initialization code. */ #line 66 "parser.y" { yylloc.first_line = 1; yylloc.first_column = 0; yylloc.last_line = 1; yylloc.last_column = 0; yylloc.filename = augl_get_info(scanner)->filename; yylloc.error = augl_get_info(scanner)->error; } #line 1633 "parser.c" yylsp[0] = yylloc; goto yysetstate; /*------------------------------------------------------------. | yynewstate -- push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; /*--------------------------------------------------------------------. | yysetstate -- set current state (the top of the stack) to yystate. | `--------------------------------------------------------------------*/ yysetstate: YYDPRINTF ((stderr, "Entering state %d\n", yystate)); YY_ASSERT (0 <= yystate && yystate < YYNSTATES); YY_IGNORE_USELESS_CAST_BEGIN *yyssp = YY_CAST (yy_state_t, yystate); YY_IGNORE_USELESS_CAST_END YY_STACK_PRINT (yyss, yyssp); if (yyss + yystacksize - 1 <= yyssp) #if !defined yyoverflow && !defined YYSTACK_RELOCATE YYNOMEM; #else { /* Get the current used size of the three stacks, in elements. */ YYPTRDIFF_T yysize = yyssp - yyss + 1; # if defined yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ yy_state_t *yyss1 = yyss; YYSTYPE *yyvs1 = yyvs; YYLTYPE *yyls1 = yyls; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * YYSIZEOF (*yyssp), &yyvs1, yysize * YYSIZEOF (*yyvsp), &yyls1, yysize * YYSIZEOF (*yylsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; yyls = yyls1; } # else /* defined YYSTACK_RELOCATE */ /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) YYNOMEM; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yy_state_t *yyss1 = yyss; union yyalloc *yyptr = YY_CAST (union yyalloc *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); if (! yyptr) YYNOMEM; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); YYSTACK_RELOCATE (yyls_alloc, yyls); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; yylsp = yyls + yysize - 1; YY_IGNORE_USELESS_CAST_BEGIN YYDPRINTF ((stderr, "Stack size increased to %ld\n", YY_CAST (long, yystacksize))); YY_IGNORE_USELESS_CAST_END if (yyss + yystacksize - 1 <= yyssp) YYABORT; } #endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token\n")); yychar = yylex (&yylval, &yylloc, scanner); } if (yychar <= YYEOF) { yychar = YYEOF; yytoken = YYSYMBOL_YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else if (yychar == YYerror) { /* The scanner already issued an error message, process directly to error recovery. But do not keep the error token as lookahead, it is too special and may lead us to an endless loop in error recovery. */ yychar = YYUNDEF; yytoken = YYSYMBOL_YYerror; yyerror_range[1] = yylloc; goto yyerrlab1; } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yytable_value_is_error (yyn)) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); yystate = yyn; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END *++yylsp = yylloc; /* Discard the shifted token. */ yychar = YYEMPTY; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: '$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; /* Default location. */ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); yyerror_range[1] = yyloc; YY_REDUCE_PRINT (yyn); switch (yyn) { case 2: /* start: KW_MODULE UIDENT '=' autoload decls */ #line 180 "parser.y" { (*term) = make_module((yyvsp[-3].string), (yyvsp[-1].string), (yyvsp[0].term), &(yylsp[-4])); } #line 1846 "parser.c" break; case 3: /* autoload: KW_AUTOLOAD LIDENT */ #line 183 "parser.y" { (yyval.string) = (yyvsp[0].string); } #line 1852 "parser.c" break; case 4: /* autoload: %empty */ #line 185 "parser.y" { (yyval.string) = NULL; } #line 1858 "parser.c" break; case 5: /* decls: KW_LET LIDENT param_list '=' exp decls */ #line 188 "parser.y" { LOC_MERGE((yylsp[-5]), (yylsp[-5]), (yylsp[-1])); (yyval.term) = make_bind((yyvsp[-4].string), (yyvsp[-3].term), (yyvsp[-1].term), (yyvsp[0].term), &(yylsp[-5])); } #line 1867 "parser.c" break; case 6: /* decls: KW_LET_REC LIDENT '=' exp decls */ #line 193 "parser.y" { LOC_MERGE((yylsp[-4]), (yylsp[-4]), (yylsp[-1])); (yyval.term) = make_bind_rec((yyvsp[-3].string), (yyvsp[-1].term), (yyvsp[0].term), &(yylsp[-4])); } #line 1876 "parser.c" break; case 7: /* decls: KW_TEST test_exp '=' exp decls */ #line 198 "parser.y" { LOC_MERGE((yylsp[-4]), (yylsp[-4]), (yylsp[-1])); (yyval.term) = make_test((yyvsp[-3].term), (yyvsp[-1].term), TR_CHECK, (yyvsp[0].term), &(yylsp[-4])); } #line 1885 "parser.c" break; case 8: /* decls: KW_TEST test_exp '=' test_special_res decls */ #line 203 "parser.y" { LOC_MERGE((yylsp[-4]), (yylsp[-4]), (yylsp[-1])); (yyval.term) = make_test((yyvsp[-3].term), NULL, (yyvsp[-1].intval), (yyvsp[0].term), &(yylsp[-4])); } #line 1894 "parser.c" break; case 9: /* decls: %empty */ #line 208 "parser.y" { (yyval.term) = NULL; } #line 1900 "parser.c" break; case 10: /* test_exp: aexp KW_GET exp */ #line 213 "parser.y" { (yyval.term) = make_get_test((yyvsp[-2].term), (yyvsp[0].term), &(yyloc)); } #line 1906 "parser.c" break; case 11: /* test_exp: aexp KW_PUT aexp KW_AFTER exp */ #line 215 "parser.y" { (yyval.term) = make_put_test((yyvsp[-4].term), (yyvsp[-2].term), (yyvsp[0].term), &(yyloc)); } #line 1912 "parser.c" break; case 12: /* test_special_res: '?' */ #line 218 "parser.y" { (yyval.intval) = TR_PRINT; } #line 1918 "parser.c" break; case 13: /* test_special_res: '*' */ #line 220 "parser.y" { (yyval.intval) = TR_EXN; } #line 1924 "parser.c" break; case 14: /* exp: KW_LET LIDENT param_list '=' exp KW_IN exp */ #line 224 "parser.y" { LOC_MERGE((yylsp[-6]), (yylsp[-6]), (yylsp[-1])); (yyval.term) = make_let((yyvsp[-5].string), (yyvsp[-4].term), (yyvsp[-2].term), (yyvsp[0].term), &(yylsp[-6])); } #line 1933 "parser.c" break; case 16: /* composeexp: composeexp ';' unionexp */ #line 231 "parser.y" { (yyval.term) = make_binop(A_COMPOSE, (yyvsp[-2].term), (yyvsp[0].term), &(yyloc)); } #line 1939 "parser.c" break; case 17: /* composeexp: unionexp */ #line 233 "parser.y" { (yyval.term) = (yyvsp[0].term); } #line 1945 "parser.c" break; case 18: /* unionexp: unionexp '|' minusexp */ #line 236 "parser.y" { (yyval.term) = make_binop(A_UNION, (yyvsp[-2].term), (yyvsp[0].term), &(yyloc)); } #line 1951 "parser.c" break; case 19: /* unionexp: minusexp */ #line 238 "parser.y" { (yyval.term) = (yyvsp[0].term); } #line 1957 "parser.c" break; case 20: /* unionexp: tree_const */ #line 240 "parser.y" { (yyval.term) = make_tree_value((yyvsp[0].tree), &(yylsp[0])); } #line 1963 "parser.c" break; case 21: /* minusexp: minusexp '-' catexp */ #line 243 "parser.y" { (yyval.term) = make_binop(A_MINUS, (yyvsp[-2].term), (yyvsp[0].term), &(yyloc)); } #line 1969 "parser.c" break; case 22: /* minusexp: catexp */ #line 245 "parser.y" { (yyval.term) = (yyvsp[0].term); } #line 1975 "parser.c" break; case 23: /* catexp: catexp '.' appexp */ #line 248 "parser.y" { (yyval.term) = make_binop(A_CONCAT, (yyvsp[-2].term), (yyvsp[0].term), &(yyloc)); } #line 1981 "parser.c" break; case 24: /* catexp: appexp */ #line 250 "parser.y" { (yyval.term) = (yyvsp[0].term); } #line 1987 "parser.c" break; case 25: /* appexp: appexp rexp */ #line 253 "parser.y" { (yyval.term) = make_binop(A_APP, (yyvsp[-1].term), (yyvsp[0].term), &(yyloc)); } #line 1993 "parser.c" break; case 26: /* appexp: rexp */ #line 255 "parser.y" { (yyval.term) = (yyvsp[0].term); } #line 1999 "parser.c" break; case 27: /* aexp: qid */ #line 258 "parser.y" { (yyval.term) = make_ident((yyvsp[0].string), &(yylsp[0])); } #line 2005 "parser.c" break; case 28: /* aexp: DQUOTED */ #line 260 "parser.y" { (yyval.term) = make_string_term((yyvsp[0].string), &(yylsp[0])); } #line 2011 "parser.c" break; case 29: /* aexp: REGEXP */ #line 262 "parser.y" { (yyval.term) = make_regexp_term((yyvsp[0].regexp).pattern, (yyvsp[0].regexp).nocase, &(yylsp[0])); } #line 2017 "parser.c" break; case 30: /* aexp: '(' exp ')' */ #line 264 "parser.y" { (yyval.term) = (yyvsp[-1].term); } #line 2023 "parser.c" break; case 31: /* aexp: '[' exp ']' */ #line 266 "parser.y" { (yyval.term) = make_unop(A_BRACKET, (yyvsp[-1].term), &(yyloc)); } #line 2029 "parser.c" break; case 32: /* aexp: '(' ')' */ #line 268 "parser.y" { (yyval.term) = make_unit_term(&(yyloc)); } #line 2035 "parser.c" break; case 33: /* rexp: aexp rep */ #line 271 "parser.y" { (yyval.term) = make_rep((yyvsp[-1].term), (yyvsp[0].quant), &(yyloc)); } #line 2041 "parser.c" break; case 34: /* rexp: aexp */ #line 273 "parser.y" { (yyval.term) = (yyvsp[0].term); } #line 2047 "parser.c" break; case 35: /* rep: '*' */ #line 276 "parser.y" { (yyval.quant) = Q_STAR; } #line 2053 "parser.c" break; case 36: /* rep: '+' */ #line 278 "parser.y" { (yyval.quant) = Q_PLUS; } #line 2059 "parser.c" break; case 37: /* rep: '?' */ #line 280 "parser.y" { (yyval.quant) = Q_MAYBE; } #line 2065 "parser.c" break; case 38: /* qid: LIDENT */ #line 283 "parser.y" { (yyval.string) = (yyvsp[0].string); } #line 2071 "parser.c" break; case 39: /* qid: QIDENT */ #line 285 "parser.y" { (yyval.string) = (yyvsp[0].string); } #line 2077 "parser.c" break; case 40: /* qid: KW_GET */ #line 287 "parser.y" { (yyval.string) = strdup("get"); } #line 2083 "parser.c" break; case 41: /* qid: KW_PUT */ #line 289 "parser.y" { (yyval.string) = strdup("put"); } #line 2089 "parser.c" break; case 42: /* param_list: param param_list */ #line 292 "parser.y" { (yyval.term) = (yyvsp[0].term); list_cons((yyval.term), (yyvsp[-1].term)); } #line 2095 "parser.c" break; case 43: /* param_list: %empty */ #line 294 "parser.y" { (yyval.term) = NULL; } #line 2101 "parser.c" break; case 44: /* param: '(' id ':' type ')' */ #line 297 "parser.y" { (yyval.term) = make_param((yyvsp[-3].string), (yyvsp[-1].type), clone_info(&(yylsp[-4]))); } #line 2107 "parser.c" break; case 45: /* id: LIDENT */ #line 300 "parser.y" { (yyval.string) = (yyvsp[0].string); } #line 2113 "parser.c" break; case 46: /* id: KW_GET */ #line 302 "parser.y" { (yyval.string) = strdup("get"); } #line 2119 "parser.c" break; case 47: /* id: KW_PUT */ #line 304 "parser.y" { (yyval.string) = strdup("put"); } #line 2125 "parser.c" break; case 48: /* type: atype ARROW type */ #line 307 "parser.y" { (yyval.type) = make_arrow_type((yyvsp[-2].type), (yyvsp[0].type)); } #line 2131 "parser.c" break; case 49: /* type: atype */ #line 309 "parser.y" { (yyval.type) = (yyvsp[0].type); } #line 2137 "parser.c" break; case 50: /* atype: KW_STRING */ #line 312 "parser.y" { (yyval.type) = make_base_type(T_STRING); } #line 2143 "parser.c" break; case 51: /* atype: KW_REGEXP */ #line 314 "parser.y" { (yyval.type) = make_base_type(T_REGEXP); } #line 2149 "parser.c" break; case 52: /* atype: KW_LENS */ #line 316 "parser.y" { (yyval.type) = make_base_type(T_LENS); } #line 2155 "parser.c" break; case 53: /* atype: '(' type ')' */ #line 318 "parser.y" { (yyval.type) = (yyvsp[-1].type); } #line 2161 "parser.c" break; case 54: /* tree_const: tree_const '{' tree_branch '}' */ #line 321 "parser.y" { (yyval.tree) = tree_concat((yyvsp[-3].tree), (yyvsp[-1].tree)); } #line 2167 "parser.c" break; case 55: /* tree_const: '{' tree_branch '}' */ #line 323 "parser.y" { (yyval.tree) = tree_concat((yyvsp[-1].tree), NULL); } #line 2173 "parser.c" break; case 56: /* tree_const2: tree_const2 '{' tree_branch '}' */ #line 326 "parser.y" { (yyval.tree) = tree_concat((yyvsp[-3].tree), (yyvsp[-1].tree)); } #line 2181 "parser.c" break; case 57: /* tree_const2: %empty */ #line 330 "parser.y" { (yyval.tree) = NULL; } #line 2187 "parser.c" break; case 58: /* tree_branch: tree_label tree_const2 */ #line 333 "parser.y" { (yyval.tree) = make_tree((yyvsp[-1].string), NULL, NULL, (yyvsp[0].tree)); } #line 2195 "parser.c" break; case 59: /* tree_branch: tree_label '=' DQUOTED tree_const2 */ #line 337 "parser.y" { (yyval.tree) = make_tree((yyvsp[-3].string), (yyvsp[-1].string), NULL, (yyvsp[0].tree)); } #line 2203 "parser.c" break; case 61: /* tree_label: %empty */ #line 342 "parser.y" { (yyval.string) = NULL; } #line 2209 "parser.c" break; #line 2213 "parser.c" default: break; } /* User semantic actions sometimes alter yychar, and that requires that yytoken be updated with the new translation. We take the approach of translating immediately before every use of yytoken. One alternative is translating here after every semantic action, but that translation would be missed if the semantic action invokes YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an incorrect destructor might then be invoked immediately. In the case of YYERROR or YYBACKUP, subsequent parser actions might lead to an incorrect destructor call or verbose syntax error message before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; *++yyvsp = yyval; *++yylsp = yyloc; /* Now 'shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ { const int yylhs = yyr1[yyn] - YYNTOKENS; const int yyi = yypgoto[yylhs] + *yyssp; yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp ? yytable[yyi] : yydefgoto[yylhs]); } goto yynewstate; /*--------------------------------------. | yyerrlab -- here on detecting error. | `--------------------------------------*/ yyerrlab: /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; { yypcontext_t yyctx = {yyssp, yytoken, &yylloc}; char const *yymsgp = YY_("syntax error"); int yysyntax_error_status; yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); if (yysyntax_error_status == 0) yymsgp = yymsg; else if (yysyntax_error_status == -1) { if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); if (yymsg) { yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); yymsgp = yymsg; } else { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; yysyntax_error_status = YYENOMEM; } } yyerror (&yylloc, term, scanner, yymsgp); if (yysyntax_error_status == YYENOMEM) YYNOMEM; } } yyerror_range[1] = yylloc; if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval, &yylloc, term, scanner); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (0) YYERROR; ++yynerrs; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ /* Pop stack until we find a state that shifts the error token. */ for (;;) { yyn = yypact[yystate]; if (!yypact_value_is_default (yyn)) { yyn += YYSYMBOL_YYerror; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yyerror_range[1] = *yylsp; yydestruct ("Error: popping", YY_ACCESSING_SYMBOL (yystate), yyvsp, yylsp, term, scanner); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END yyerror_range[2] = yylloc; ++yylsp; YYLLOC_DEFAULT (*yylsp, yyerror_range, 2); /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturnlab; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturnlab; /*-----------------------------------------------------------. | yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | `-----------------------------------------------------------*/ yyexhaustedlab: yyerror (&yylloc, term, scanner, YY_("memory exhausted")); yyresult = 2; goto yyreturnlab; /*----------------------------------------------------------. | yyreturnlab -- parsing is finished, clean up and return. | `----------------------------------------------------------*/ yyreturnlab: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = YYTRANSLATE (yychar); yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval, &yylloc, term, scanner); } /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, yylsp, term, scanner); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); return yyresult; } #line 343 "parser.y" int augl_parse_file(struct augeas *aug, const char *name, struct term **term) { yyscan_t scanner; struct state state; struct string *sname = NULL; struct info info; int result = -1; int r; *term = NULL; r = make_ref(sname); ERR_NOMEM(r < 0, aug); sname->str = strdup(name); ERR_NOMEM(sname->str == NULL, aug); MEMZERO(&info, 1); info.ref = UINT_MAX; info.filename = sname; info.error = aug->error; MEMZERO(&state, 1); state.info = &info; state.comment_depth = 0; if (augl_init_lexer(&state, &scanner) < 0) { augl_error(&info, term, NULL, "file not found"); goto error; } yydebug = getenv("YYDEBUG") != NULL; r = augl_parse(term, scanner); augl_close_lexer(scanner); augl_lex_destroy(scanner); if (r == 1) { augl_error(&info, term, NULL, "syntax error"); goto error; } else if (r == 2) { augl_error(&info, term, NULL, "parser ran out of memory"); ERR_NOMEM(1, aug); } result = 0; error: unref(sname, string); // free TERM return result; } // FIXME: Nothing here checks for alloc errors. static struct info *clone_info(struct info *locp) { struct info *info; make_ref(info); info->filename = ref(locp->filename); info->first_line = locp->first_line; info->first_column = locp->first_column; info->last_line = locp->last_line; info->last_column = locp->last_column; info->error = locp->error; return info; } static struct term *make_term_locp(enum term_tag tag, struct info *locp) { struct info *info = clone_info(locp); return make_term(tag, info); } static struct term *make_module(char *ident, char *autoload, struct term *decls, struct info *locp) { struct term *term = make_term_locp(A_MODULE, locp); term->mname = ident; term->autoload = autoload; term->decls = decls; return term; } static struct term *make_bind(char *ident, struct term *params, struct term *exp, struct term *decls, struct info *locp) { struct term *term = make_term_locp(A_BIND, locp); if (params != NULL) exp = build_func(params, exp); term->bname = ident; term->exp = exp; list_cons(decls, term); return decls; } static struct term *make_bind_rec(char *ident, struct term *exp, struct term *decls, struct info *locp) { /* Desugar let rec IDENT = EXP as * let IDENT = * let RLENS = (lns_make_rec) in * lns_check_rec ((lambda IDENT: EXP) RLENS) RLENS * where RLENS is a brandnew recursive lens. * * That only works since we know that 'let rec' is only defined for lenses, * not general purposes functions, i.e. we know that IDENT has type 'lens' * * The point of all this is that we make it possible to put a recursive * lens (which is a placeholder for the actual recursion) into arbitrary * places in some bigger lens and then have LNS_CHECK_REC rattle through * to do the special-purpose typechecking. */ char *id; struct info *info = exp->info; struct term *lambda = NULL, *rlens = NULL; struct term *app1 = NULL, *app2 = NULL, *app3 = NULL; id = strdup(ident); if (id == NULL) goto error; lambda = make_param(id, make_base_type(T_LENS), ref(info)); if (lambda == NULL) goto error; id = NULL; build_func(lambda, exp); rlens = make_term(A_VALUE, ref(exp->info)); if (rlens == NULL) goto error; rlens->value = lns_make_rec(ref(exp->info)); if (rlens->value == NULL) goto error; rlens->type = make_base_type(T_LENS); app1 = make_app_term(lambda, rlens, ref(info)); if (app1 == NULL) goto error; id = strdup(LNS_CHECK_REC_NAME); if (id == NULL) goto error; app2 = make_app_ident(id, app1, ref(info)); if (app2 == NULL) goto error; id = NULL; app3 = make_app_term(app2, ref(rlens), ref(info)); if (app3 == NULL) goto error; return make_bind(ident, NULL, app3, decls, locp); error: free(id); unref(lambda, term); unref(rlens, term); unref(app1, term); unref(app2, term); unref(app3, term); return NULL; } static struct term *make_let(char *ident, struct term *params, struct term *exp, struct term *body, struct info *locp) { /* let f (x:string) = "f " . x in f "a" . f "b" */ /* (lambda f: f "a" . f "b") (lambda x: "f " . x) */ /* (lambda IDENT: BODY) (lambda PARAMS: EXP) */ /* Desugar as (lambda IDENT: BODY) (lambda PARAMS: EXP) */ struct term *term = make_term_locp(A_LET, locp); struct term *p = make_param(ident, NULL, ref(term->info)); term->left = build_func(p, body); if (params != NULL) term->right = build_func(params, exp); else term->right = exp; return term; } static struct term *make_binop(enum term_tag tag, struct term *left, struct term *right, struct info *locp) { assert(tag == A_COMPOSE || tag == A_CONCAT || tag == A_UNION || tag == A_APP || tag == A_MINUS); struct term *term = make_term_locp(tag, locp); term->left = left; term->right = right; return term; } static struct term *make_unop(enum term_tag tag, struct term *exp, struct info *locp) { assert(tag == A_BRACKET); struct term *term = make_term_locp(tag, locp); term->brexp = exp; return term; } static struct term *make_ident(char *qname, struct info *locp) { struct term *term = make_term_locp(A_IDENT, locp); term->ident = make_string(qname); return term; } static struct term *make_unit_term(struct info *locp) { struct term *term = make_term_locp(A_VALUE, locp); term->value = make_unit(ref(term->info)); return term; } static struct term *make_string_term(char *value, struct info *locp) { struct term *term = make_term_locp(A_VALUE, locp); term->value = make_value(V_STRING, ref(term->info)); term->value->string = make_string(value); return term; } static struct term *make_regexp_term(char *pattern, int nocase, struct info *locp) { struct term *term = make_term_locp(A_VALUE, locp); term->value = make_value(V_REGEXP, ref(term->info)); term->value->regexp = make_regexp(term->info, pattern, nocase); return term; } static struct term *make_rep(struct term *exp, enum quant_tag quant, struct info *locp) { struct term *term = make_term_locp(A_REP, locp); term->quant = quant; term->exp = exp; return term; } static struct term *make_get_test(struct term *lens, struct term *arg, struct info *locp) { /* Return a term for "get" LENS ARG */ struct info *info = clone_info(locp); struct term *term = make_app_ident(strdup("get"), lens, info); term = make_app_term(term, arg, ref(info)); return term; } static struct term *make_put_test(struct term *lens, struct term *arg, struct term *cmds, struct info *locp) { /* Return a term for "put" LENS (CMDS ("get" LENS ARG)) ARG */ struct term *term = make_get_test(lens, arg, locp); term = make_app_term(cmds, term, ref(term->info)); struct term *put = make_app_ident(strdup("put"), ref(lens), ref(term->info)); put = make_app_term(put, term, ref(term->info)); put = make_app_term(put, ref(arg), ref(term->info)); return put; } static struct term *make_test(struct term *test, struct term *result, enum test_result_tag tr_tag, struct term *decls, struct info *locp) { struct term *term = make_term_locp(A_TEST, locp); term->tr_tag = tr_tag; term->test = test; term->result = result; term->next = decls; return term; } static struct term *make_tree_value(struct tree *tree, struct info *locp) { struct term *term = make_term_locp(A_VALUE, locp); struct value *value = make_value(V_TREE, ref(term->info)); value->origin = make_tree_origin(tree); term->value = value; return term; } static struct tree *tree_concat(struct tree *t1, struct tree *t2) { if (t2 != NULL) list_append(t1, t2); return t1; } void augl_error(struct info *locp, struct term **term, yyscan_t scanner, const char *s) { struct info info; struct string string; MEMZERO(&info, 1); info.ref = string.ref = UINT_MAX; info.filename = &string; if (locp != NULL) { info.first_line = locp->first_line; info.first_column = locp->first_column; info.last_line = locp->last_line; info.last_column = locp->last_column; info.filename->str = locp->filename->str; info.error = locp->error; } else if (scanner != NULL) { info.first_line = augl_get_lineno(scanner); info.first_column = augl_get_column(scanner); info.last_line = augl_get_lineno(scanner); info.last_column = augl_get_column(scanner); info.filename = augl_get_info(scanner)->filename; info.error = augl_get_info(scanner)->error; } else if (*term != NULL && (*term)->info != NULL) { memcpy(&info, (*term)->info, sizeof(info)); } else { info.first_line = info.last_line = 0; info.first_column = info.last_column = 0; } syntax_error(&info, "%s", s); } augeas-1.13.0/src/jmt.h0000644000175000017500000000376214161102026011515 00000000000000/* * jmt.h: Earley parser for lenses based on Jim/Mandelbaum transducers * * Copyright (C) 2009-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #ifndef EARLEY_H_ #define EARLEY_H_ #include #include #include "lens.h" struct jmt; struct jmt_parse; typedef uint32_t ind_t; struct lens; typedef void (*jmt_traverser)(struct lens *l, size_t start, size_t end, void *data); typedef void (*jmt_error)(struct lens *lens, void *data, size_t pos, const char *format, ...); struct jmt_visitor { struct jmt_parse *parse; jmt_traverser terminal; jmt_traverser enter; jmt_traverser exit; jmt_error error; void *data; }; struct jmt *jmt_build(struct lens *l); struct jmt_parse *jmt_parse(struct jmt *jmt, const char *text, size_t text_len); void jmt_free_parse(struct jmt_parse *); /* Returns -1 on internal error, 0 on syntax error, 1 on a successful * parse. */ int jmt_visit(struct jmt_visitor *visitor, size_t *len); void jmt_free(struct jmt *jmt); void jmt_dot(struct jmt *jmt, const char *fname); #endif /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/syntax.c0000644000175000017500000016501414161102026012243 00000000000000/* * syntax.c: * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include #include #include #include #include #include #include #include #include #include "memory.h" #include "syntax.h" #include "augeas.h" #include "transform.h" #include "errcode.h" /* Extension of source files */ #define AUG_EXT ".aug" #define LNS_TYPE_CHECK(ctx) ((ctx)->aug->flags & AUG_TYPE_CHECK) static const char *const builtin_module = "Builtin"; static const struct type string_type = { .ref = UINT_MAX, .tag = T_STRING }; static const struct type regexp_type = { .ref = UINT_MAX, .tag = T_REGEXP }; static const struct type lens_type = { .ref = UINT_MAX, .tag = T_LENS }; static const struct type tree_type = { .ref = UINT_MAX, .tag = T_TREE }; static const struct type filter_type = { .ref = UINT_MAX, .tag = T_FILTER }; static const struct type transform_type = { .ref = UINT_MAX, .tag = T_TRANSFORM }; static const struct type unit_type = { .ref = UINT_MAX, .tag = T_UNIT }; const struct type *const t_string = &string_type; const struct type *const t_regexp = ®exp_type; const struct type *const t_lens = &lens_type; const struct type *const t_tree = &tree_type; const struct type *const t_filter = &filter_type; const struct type *const t_transform = &transform_type; const struct type *const t_unit = &unit_type; static const char *const type_names[] = { "string", "regexp", "lens", "tree", "filter", "transform", "function", "unit", NULL }; /* The anonymous identifier which we will never bind */ static const char anon_ident[] = "_"; static void print_value(FILE *out, struct value *v); /* The evaluation context with all loaded modules and the bindings for the * module we are working on in LOCAL */ struct ctx { const char *name; /* The module we are working on */ struct augeas *aug; struct binding *local; }; static int init_fatal_exn(struct error *error) { if (error->exn != NULL) return 0; error->exn = make_exn_value(ref(error->info), "Error during evaluation"); if (error->exn == NULL) return -1; error->exn->exn->seen = 1; error->exn->exn->error = 1; error->exn->exn->lines = NULL; error->exn->exn->nlines = 0; error->exn->ref = REF_MAX; return 0; } static void format_error(struct info *info, aug_errcode_t code, const char *format, va_list ap) { struct error *error = info->error; char *si = NULL, *sf = NULL, *sd = NULL; int r; error->code = code; /* Only syntax errors are cumulative */ if (code != AUG_ESYNTAX) FREE(error->details); si = format_info(info); r = vasprintf(&sf, format, ap); if (r < 0) sf = NULL; if (error->details != NULL) { r = xasprintf(&sd, "%s\n%s%s", error->details, (si == NULL) ? "(no location)" : si, (sf == NULL) ? "(no details)" : sf); } else { r = xasprintf(&sd, "%s%s", (si == NULL) ? "(no location)" : si, (sf == NULL) ? "(no details)" : sf); } if (r >= 0) { free(error->details); error->details = sd; } free(si); free(sf); } void syntax_error(struct info *info, const char *format, ...) { struct error *error = info->error; va_list ap; if (error->code != AUG_NOERROR && error->code != AUG_ESYNTAX) return; va_start(ap, format); format_error(info, AUG_ESYNTAX, format, ap); va_end(ap); } void fatal_error(struct info *info, const char *format, ...) { struct error *error = info->error; va_list ap; if (error->code == AUG_EINTERNAL) return; va_start(ap, format); format_error(info, AUG_EINTERNAL, format, ap); va_end(ap); } static void free_param(struct param *param) { if (param == NULL) return; assert(param->ref == 0); unref(param->info, info); unref(param->name, string); unref(param->type, type); free(param); } void free_term(struct term *term) { if (term == NULL) return; assert(term->ref == 0); switch(term->tag) { case A_MODULE: free(term->mname); free(term->autoload); unref(term->decls, term); break; case A_BIND: free(term->bname); unref(term->exp, term); break; case A_COMPOSE: case A_UNION: case A_MINUS: case A_CONCAT: case A_APP: case A_LET: unref(term->left, term); unref(term->right, term); break; case A_VALUE: unref(term->value, value); break; case A_IDENT: unref(term->ident, string); break; case A_BRACKET: unref(term->brexp, term); break; case A_FUNC: unref(term->param, param); unref(term->body, term); break; case A_REP: unref(term->rexp, term); break; case A_TEST: unref(term->test, term); unref(term->result, term); break; default: assert(0); break; } unref(term->next, term); unref(term->info, info); unref(term->type, type); free(term); } static void free_binding(struct binding *binding) { if (binding == NULL) return; assert(binding->ref == 0); unref(binding->next, binding); unref(binding->ident, string); unref(binding->type, type); unref(binding->value, value); free(binding); } void free_module(struct module *module) { if (module == NULL) return; assert(module->ref == 0); free(module->name); unref(module->next, module); unref(module->bindings, binding); unref(module->autoload, transform); free(module); } void free_type(struct type *type) { if (type == NULL) return; assert(type->ref == 0); if (type->tag == T_ARROW) { unref(type->dom, type); unref(type->img, type); } free(type); } static void free_exn(struct exn *exn) { if (exn == NULL) return; unref(exn->info, info); free(exn->message); for (int i=0; i < exn->nlines; i++) { free(exn->lines[i]); } free(exn->lines); free(exn); } void free_value(struct value *v) { if (v == NULL) return; assert(v->ref == 0); switch(v->tag) { case V_STRING: unref(v->string, string); break; case V_REGEXP: unref(v->regexp, regexp); break; case V_LENS: unref(v->lens, lens); break; case V_TREE: free_tree(v->origin); break; case V_FILTER: unref(v->filter, filter); break; case V_TRANSFORM: unref(v->transform, transform); break; case V_NATIVE: if (v->native) unref(v->native->type, type); free(v->native); break; case V_CLOS: unref(v->func, term); unref(v->bindings, binding); break; case V_EXN: free_exn(v->exn); break; case V_UNIT: break; default: assert(0); } unref(v->info, info); free(v); } /* * Creation of (some) terms. Others are in parser.y * Reference counted arguments are now owned by the returned object, i.e. * the make_* functions do not increment the count. * Returned objects have a referece count of 1. */ struct term *make_term(enum term_tag tag, struct info *info) { struct term *term; if (make_ref(term) < 0) { unref(info, info); } else { term->tag = tag; term->info = info; } return term; } struct term *make_param(char *name, struct type *type, struct info *info) { struct term *term = make_term(A_FUNC, info); if (term == NULL) goto error; make_ref_err(term->param); term->param->info = ref(term->info); make_ref_err(term->param->name); term->param->name->str = name; term->param->type = type; return term; error: unref(term, term); return NULL; } struct value *make_value(enum value_tag tag, struct info *info) { struct value *value = NULL; if (make_ref(value) < 0) { unref(info, info); } else { value->tag = tag; value->info = info; } return value; } struct value *make_unit(struct info *info) { return make_value(V_UNIT, info); } struct term *make_app_term(struct term *lambda, struct term *arg, struct info *info) { struct term *app = make_term(A_APP, info); if (app == NULL) { unref(lambda, term); unref(arg, term); } else { app->left = lambda; app->right = arg; } return app; } struct term *make_app_ident(char *id, struct term *arg, struct info *info) { struct term *ident = make_term(A_IDENT, ref(info)); ident->ident = make_string(id); if (ident->ident == NULL) { unref(arg, term); unref(info, info); unref(ident, term); return NULL; } return make_app_term(ident, arg, info); } struct term *build_func(struct term *params, struct term *exp) { assert(params->tag == A_FUNC); if (params->next != NULL) exp = build_func(params->next, exp); params->body = exp; params->next = NULL; return params; } /* Ownership is taken as needed */ static struct value *make_closure(struct term *func, struct binding *bnds) { struct value *v = NULL; if (make_ref(v) == 0) { v->tag = V_CLOS; v->info = ref(func->info); v->func = ref(func); v->bindings = ref(bnds); } return v; } struct value *make_exn_value(struct info *info, const char *format, ...) { va_list ap; int r; struct value *v; char *message; va_start(ap, format); r = vasprintf(&message, format, ap); va_end(ap); if (r == -1) return NULL; v = make_value(V_EXN, ref(info)); if (ALLOC(v->exn) < 0) return info->error->exn; v->exn->info = info; v->exn->message = message; return v; } void exn_add_lines(struct value *v, int nlines, ...) { assert(v->tag == V_EXN); va_list ap; if (REALLOC_N(v->exn->lines, v->exn->nlines + nlines) == -1) return; va_start(ap, nlines); for (int i=0; i < nlines; i++) { char *line = va_arg(ap, char *); v->exn->lines[v->exn->nlines + i] = line; } va_end(ap); v->exn->nlines += nlines; } void exn_printf_line(struct value *exn, const char *format, ...) { va_list ap; int r; char *line; va_start(ap, format); r = vasprintf(&line, format, ap); va_end(ap); if (r >= 0) exn_add_lines(exn, 1, line); } /* * Modules */ static int load_module(struct augeas *aug, const char *name); static char *module_basename(const char *modname); struct module *module_create(const char *name) { struct module *module; make_ref(module); module->name = strdup(name); return module; } static struct module *module_find(struct module *module, const char *name) { list_for_each(e, module) { if (STRCASEEQ(e->name, name)) return e; } return NULL; } static struct binding *bnd_lookup(struct binding *bindings, const char *name) { list_for_each(b, bindings) { if (STREQ(b->ident->str, name)) return b; } return NULL; } static char *modname_of_qname(const char *qname) { char *dot = strchr(qname, '.'); if (dot == NULL) return NULL; return strndup(qname, dot - qname); } static int lookup_internal(struct augeas *aug, const char *ctx_modname, const char *name, struct binding **bnd) { char *modname = modname_of_qname(name); *bnd = NULL; if (modname == NULL) { struct module *builtin = module_find(aug->modules, builtin_module); assert(builtin != NULL); *bnd = bnd_lookup(builtin->bindings, name); return 0; } qual_lookup: list_for_each(module, aug->modules) { if (STRCASEEQ(module->name, modname)) { *bnd = bnd_lookup(module->bindings, name + strlen(modname) + 1); free(modname); return 0; } } /* Try to load the module */ if (streqv(modname, ctx_modname)) { free(modname); return 0; } int loaded = load_module(aug, modname) == 0; if (loaded) goto qual_lookup; free(modname); return -1; } struct lens *lens_lookup(struct augeas *aug, const char *qname) { struct binding *bnd = NULL; if (lookup_internal(aug, NULL, qname, &bnd) < 0) return NULL; if (bnd == NULL || bnd->value->tag != V_LENS) return NULL; return bnd->value->lens; } static struct binding *ctx_lookup_bnd(struct info *info, struct ctx *ctx, const char *name) { struct binding *b = NULL; int nlen = strlen(ctx->name); if (STREQLEN(ctx->name, name, nlen) && name[nlen] == '.') name += nlen + 1; b = bnd_lookup(ctx->local, name); if (b != NULL) return b; if (ctx->aug != NULL) { int r; r = lookup_internal(ctx->aug, ctx->name, name, &b); if (r == 0) return b; char *modname = modname_of_qname(name); syntax_error(info, "Could not load module %s for %s", modname, name); free(modname); return NULL; } return NULL; } static struct value *ctx_lookup(struct info *info, struct ctx *ctx, struct string *ident) { struct binding *b = ctx_lookup_bnd(info, ctx, ident->str); return b == NULL ? NULL : b->value; } static struct type *ctx_lookup_type(struct info *info, struct ctx *ctx, struct string *ident) { struct binding *b = ctx_lookup_bnd(info, ctx, ident->str); return b == NULL ? NULL : b->type; } /* Takes ownership as needed */ static struct binding *bind_type(struct binding **bnds, const char *name, struct type *type) { struct binding *binding; if (STREQ(name, anon_ident)) return NULL; make_ref(binding); make_ref(binding->ident); binding->ident->str = strdup(name); binding->type = ref(type); list_cons(*bnds, binding); return binding; } /* Takes ownership as needed */ static void bind_param(struct binding **bnds, struct param *param, struct value *v) { struct binding *b; make_ref(b); b->ident = ref(param->name); b->type = ref(param->type); b->value = ref(v); ref(*bnds); list_cons(*bnds, b); } static void unbind_param(struct binding **bnds, ATTRIBUTE_UNUSED struct param *param) { struct binding *b = *bnds; assert(b->ident == param->name); assert(b->next != *bnds); *bnds = b->next; unref(b, binding); } /* Takes ownership of VALUE */ static void bind(struct binding **bnds, const char *name, struct type *type, struct value *value) { struct binding *b = NULL; if (STRNEQ(name, anon_ident)) { b = bind_type(bnds, name, type); b->value = ref(value); } } /* * Some debug printing */ static char *type_string(struct type *t); static void dump_bindings(struct binding *bnds) { list_for_each(b, bnds) { char *st = type_string(b->type); fprintf(stderr, " %s: %s", b->ident->str, st); fprintf(stderr, " = "); print_value(stderr, b->value); fputc('\n', stderr); free(st); } } static void dump_module(struct module *module) { if (module == NULL) return; fprintf(stderr, "Module %s\n:", module->name); dump_bindings(module->bindings); dump_module(module->next); } ATTRIBUTE_UNUSED static void dump_ctx(struct ctx *ctx) { fprintf(stderr, "Context: %s\n", ctx->name); dump_bindings(ctx->local); if (ctx->aug != NULL) { list_for_each(m, ctx->aug->modules) dump_module(m); } } /* * Values */ void print_tree_braces(FILE *out, int indent, struct tree *tree) { if (tree == NULL) { fprintf(out, "(null tree)\n"); return; } list_for_each(t, tree) { for (int i=0; i < indent; i++) fputc(' ', out); fprintf(out, "{ "); if (t->label != NULL) fprintf(out, "\"%s\"", t->label); if (t->value != NULL) fprintf(out, " = \"%s\"", t->value); if (t->children != NULL) { fputc('\n', out); print_tree_braces(out, indent + 2, t->children); for (int i=0; i < indent; i++) fputc(' ', out); } else { fputc(' ', out); } fprintf(out, "}\n"); } } static void print_value(FILE *out, struct value *v) { if (v == NULL) { fprintf(out, ""); return; } switch(v->tag) { case V_STRING: fprintf(out, "\"%s\"", v->string->str); break; case V_REGEXP: fprintf(out, "/%s/", v->regexp->pattern->str); break; case V_LENS: fprintf(out, "lens->info); fprintf(out, ">"); break; case V_TREE: print_tree_braces(out, 0, v->origin); break; case V_FILTER: fprintf(out, "filter) { fprintf(out, "%c%s%c", f->include ? '+' : '-', f->glob->str, (f->next != NULL) ? ':' : '>'); } break; case V_TRANSFORM: fprintf(out, "transform->lens->info); fprintf(out, ">"); break; case V_NATIVE: fprintf(out, "info); fprintf(out, ">"); break; case V_CLOS: fprintf(out, "func->info); fprintf(out, ">"); break; case V_EXN: if (! v->exn->seen) { print_info(out, v->exn->info); fprintf(out, "exception: %s\n", v->exn->message); for (int i=0; i < v->exn->nlines; i++) { fprintf(out, " %s\n", v->exn->lines[i]); } v->exn->seen = 1; } break; case V_UNIT: fprintf(out, "()"); break; default: assert(0); break; } } static int value_equal(struct value *v1, struct value *v2) { if (v1 == NULL && v2 == NULL) return 1; if (v1 == NULL || v2 == NULL) return 0; if (v1->tag != v2->tag) return 0; switch (v1->tag) { case V_STRING: return STREQ(v1->string->str, v2->string->str); break; case V_REGEXP: // FIXME: Should probably build FA's and compare them return STREQ(v1->regexp->pattern->str, v2->regexp->pattern->str); break; case V_LENS: return v1->lens == v2->lens; break; case V_TREE: return tree_equal(v1->origin->children, v2->origin->children); break; case V_FILTER: return v1->filter == v2->filter; break; case V_TRANSFORM: return v1->transform == v2->transform; break; case V_NATIVE: return v1->native == v2->native; break; case V_CLOS: return v1->func == v2->func && v1->bindings == v2->bindings; break; default: assert(0); abort(); break; } } /* * Types */ struct type *make_arrow_type(struct type *dom, struct type *img) { struct type *type; make_ref(type); type->tag = T_ARROW; type->dom = ref(dom); type->img = ref(img); return type; } struct type *make_base_type(enum type_tag tag) { if (tag == T_STRING) return (struct type *) t_string; else if (tag == T_REGEXP) return (struct type *) t_regexp; else if (tag == T_LENS) return (struct type *) t_lens; else if (tag == T_TREE) return (struct type *) t_tree; else if (tag == T_FILTER) return (struct type *) t_filter; else if (tag == T_TRANSFORM) return (struct type *) t_transform; else if (tag == T_UNIT) return (struct type *) t_unit; assert(0); abort(); } static const char *type_name(struct type *t) { for (int i = 0; type_names[i] != NULL; i++) if (i == t->tag) return type_names[i]; assert(0); abort(); } static char *type_string(struct type *t) { if (t->tag == T_ARROW) { char *s = NULL; int r; char *sd = type_string(t->dom); char *si = type_string(t->img); if (t->dom->tag == T_ARROW) r = asprintf(&s, "(%s) -> %s", sd, si); else r = asprintf(&s, "%s -> %s", sd, si); free(sd); free(si); return (r == -1) ? NULL : s; } else { return strdup(type_name(t)); } } /* Decide whether T1 is a subtype of T2. The only subtype relations are * T_STRING <: T_REGEXP and the usual subtyping of functions based on * comparing domains/images * * Return 1 if T1 is a subtype of T2, 0 otherwise */ static int subtype(struct type *t1, struct type *t2) { if (t1 == t2) return 1; /* We only promote T_STRING => T_REGEXP, no automatic conversion of strings/regexps to lenses (yet) */ if (t1->tag == T_STRING) return (t2->tag == T_STRING || t2->tag == T_REGEXP); if (t1->tag == T_ARROW && t2->tag == T_ARROW) { return subtype(t2->dom, t1->dom) && subtype(t1->img, t2->img); } return t1->tag == t2->tag; } static int type_equal(struct type *t1, struct type *t2) { return (t1 == t2) || (subtype(t1, t2) && subtype(t2, t1)); } /* Return a type T with subtype(T, T1) && subtype(T, T2) */ static struct type *type_meet(struct type *t1, struct type *t2); /* Return a type T with subtype(T1, T) && subtype(T2, T) */ static struct type *type_join(struct type *t1, struct type *t2) { if (t1->tag == T_STRING) { if (t2->tag == T_STRING) return ref(t1); else if (t2->tag == T_REGEXP) return ref(t2); } else if (t1->tag == T_REGEXP) { if (t2->tag == T_STRING || t2->tag == T_REGEXP) return ref(t1); } else if (t1->tag == T_ARROW) { if (t2->tag != T_ARROW) return NULL; struct type *dom = type_meet(t1->dom, t2->dom); struct type *img = type_join(t1->img, t2->img); if (dom == NULL || img == NULL) { unref(dom, type); unref(img, type); return NULL; } return make_arrow_type(dom, img); } else if (type_equal(t1, t2)) { return ref(t1); } return NULL; } /* Return a type T with subtype(T, T1) && subtype(T, T2) */ static struct type *type_meet(struct type *t1, struct type *t2) { if (t1->tag == T_STRING) { if (t2->tag == T_STRING || t2->tag == T_REGEXP) return ref(t1); } else if (t1->tag == T_REGEXP) { if (t2->tag == T_STRING || t2->tag == T_REGEXP) return ref(t2); } else if (t1->tag == T_ARROW) { if (t2->tag != T_ARROW) return NULL; struct type *dom = type_join(t1->dom, t2->dom); struct type *img = type_meet(t1->img, t2->img); if (dom == NULL || img == NULL) { unref(dom, type); unref(img, type); return NULL; } return make_arrow_type(dom, img); } else if (type_equal(t1, t2)) { return ref(t1); } return NULL; } static struct type *value_type(struct value *v) { switch(v->tag) { case V_STRING: return make_base_type(T_STRING); case V_REGEXP: return make_base_type(T_REGEXP); case V_LENS: return make_base_type(T_LENS); case V_TREE: return make_base_type(T_TREE); case V_FILTER: return make_base_type(T_FILTER); case V_TRANSFORM: return make_base_type(T_TRANSFORM); case V_UNIT: return make_base_type(T_UNIT); case V_NATIVE: return ref(v->native->type); case V_CLOS: return ref(v->func->type); case V_EXN: /* Fail on exceptions */ default: assert(0); abort(); } } /* Coerce V to the type T. Currently, only T_STRING can be coerced to * T_REGEXP. Returns a value that is owned by the caller. Trying to perform * an impossible coercion is a fatal error. Receives ownership of V. */ static struct value *coerce(struct value *v, struct type *t) { struct type *vt = value_type(v); if (type_equal(vt, t)) { unref(vt, type); return v; } if (vt->tag == T_STRING && t->tag == T_REGEXP) { struct value *rxp = make_value(V_REGEXP, ref(v->info)); rxp->regexp = make_regexp_literal(v->info, v->string->str); if (rxp->regexp == NULL) { report_error(v->info->error, AUG_ENOMEM, NULL); }; unref(v, value); unref(vt, type); return rxp; } return make_exn_value(v->info, "Type %s can not be coerced to %s", type_name(vt), type_name(t)); } /* Return one of the expected types (passed as ...). Does not give ownership of the returned type */ static struct type *expect_types_arr(struct info *info, struct type *act, int ntypes, struct type *allowed[]) { struct type *result = NULL; for (int i=0; i < ntypes; i++) { if (subtype(act, allowed[i])) { result = allowed[i]; break; } } if (result == NULL) { int len = 0; for (int i=0; i < ntypes; i++) { len += strlen(type_name(allowed[i])); } len += (ntypes - 1) * 4 + 1; char *allowed_names; if (ALLOC_N(allowed_names, len) < 0) return NULL; for (int i=0; i < ntypes; i++) { if (i > 0) strcat(allowed_names, (i == ntypes - 1) ? ", or " : ", "); strcat(allowed_names, type_name(allowed[i])); } char *act_str = type_string(act); syntax_error(info, "type error: expected %s but found %s", allowed_names, act_str); free(act_str); free(allowed_names); } return result; } static struct type *expect_types(struct info *info, struct type *act, int ntypes, ...) { va_list ap; struct type *allowed[ntypes]; va_start(ap, ntypes); for (int i=0; i < ntypes; i++) allowed[i] = va_arg(ap, struct type *); va_end(ap); return expect_types_arr(info, act, ntypes, allowed); } static struct value *apply(struct term *app, struct ctx *ctx); typedef struct value *(*impl0)(struct info *); typedef struct value *(*impl1)(struct info *, struct value *); typedef struct value *(*impl2)(struct info *, struct value *, struct value *); typedef struct value *(*impl3)(struct info *, struct value *, struct value *, struct value *); typedef struct value *(*impl4)(struct info *, struct value *, struct value *, struct value *, struct value *); typedef struct value *(*impl5)(struct info *, struct value *, struct value *, struct value *, struct value *, struct value *); static struct value *native_call(struct info *info, struct native *func, struct ctx *ctx) { struct value *argv[func->argc + 1]; struct binding *b = ctx->local; for (int i = func->argc - 1; i >= 0; i--) { argv[i] = b->value; b = b->next; } argv[func->argc] = NULL; return func->impl(info, argv); } static void type_error1(struct info *info, const char *msg, struct type *type) { char *s = type_string(type); syntax_error(info, "Type error: "); syntax_error(info, msg, s); free(s); } static void type_error2(struct info *info, const char *msg, struct type *type1, struct type *type2) { char *s1 = type_string(type1); char *s2 = type_string(type2); syntax_error(info, "Type error: "); syntax_error(info, msg, s1, s2); free(s1); free(s2); } static void type_error_binop(struct info *info, const char *opname, struct type *type1, struct type *type2) { char *s1 = type_string(type1); char *s2 = type_string(type2); syntax_error(info, "Type error: "); syntax_error(info, "%s of %s and %s is not possible", opname, s1, s2); free(s1); free(s2); } static int check_exp(struct term *term, struct ctx *ctx); static struct type *require_exp_type(struct term *term, struct ctx *ctx, int ntypes, struct type *allowed[]) { int r = 1; if (term->type == NULL) { r = check_exp(term, ctx); if (! r) return NULL; } return expect_types_arr(term->info, term->type, ntypes, allowed); } static int check_compose(struct term *term, struct ctx *ctx) { struct type *tl = NULL, *tr = NULL; if (! check_exp(term->left, ctx)) return 0; tl = term->left->type; if (tl->tag == T_ARROW) { /* Composition of functions f: a -> b and g: c -> d is defined as (f . g) x = g (f x) and is type correct if b <: c yielding a function with type a -> d */ if (! check_exp(term->right, ctx)) return 0; tr = term->right->type; if (tr->tag != T_ARROW) goto print_error; if (! subtype(tl->img, tr->dom)) goto print_error; term->type = make_arrow_type(tl->dom, tr->img); } else if (tl->tag == T_UNIT) { if (! check_exp(term->right, ctx)) return 0; term->type = ref(term->right->type); } else { goto print_error; } return 1; print_error: type_error_binop(term->info, "composition", term->left->type, term->right->type); return 0; } static int check_binop(const char *opname, struct term *term, struct ctx *ctx, int ntypes, ...) { va_list ap; struct type *allowed[ntypes]; struct type *tl = NULL, *tr = NULL; va_start(ap, ntypes); for (int i=0; i < ntypes; i++) allowed[i] = va_arg(ap, struct type *); va_end(ap); tl = require_exp_type(term->left, ctx, ntypes, allowed); if (tl == NULL) return 0; tr = require_exp_type(term->right, ctx, ntypes, allowed); if (tr == NULL) return 0; term->type = type_join(tl, tr); if (term->type == NULL) goto print_error; return 1; print_error: type_error_binop(term->info, opname, term->left->type, term->right->type); return 0; } static int check_value(struct term *term) { const char *msg; struct value *v = term->value; if (v->tag == V_REGEXP) { /* The only literal that needs checking are regular expressions, where we need to make sure the regexp is syntactically correct */ if (regexp_check(v->regexp, &msg) == -1) { syntax_error(v->info, "Invalid regular expression: %s", msg); return 0; } term->type = make_base_type(T_REGEXP); } else if (v->tag == V_EXN) { /* Exceptions can't be typed */ return 0; } else { /* There are cases where we generate values internally, and those have their type already set; we don't want to overwrite that */ if (term->type == NULL) { term->type = value_type(v); } } return 1; } /* Return 1 if TERM passes, 0 otherwise */ static int check_exp(struct term *term, struct ctx *ctx) { int result = 1; assert(term->type == NULL || term->tag == A_VALUE || term->ref > 1); if (term->type != NULL && term->tag != A_VALUE) return 1; switch (term->tag) { case A_UNION: result = check_binop("union", term, ctx, 2, t_regexp, t_lens); break; case A_MINUS: result = check_binop("minus", term, ctx, 1, t_regexp); break; case A_COMPOSE: result = check_compose(term, ctx); break; case A_CONCAT: result = check_binop("concatenation", term, ctx, 4, t_string, t_regexp, t_lens, t_filter); break; case A_LET: { result = check_exp(term->right, ctx); if (result) { struct term *func = term->left; assert(func->tag == A_FUNC); assert(func->param->type == NULL); func->param->type = ref(term->right->type); result = check_exp(func, ctx); if (result) { term->tag = A_APP; term->type = ref(func->type->img); } } } break; case A_APP: result = check_exp(term->left, ctx) & check_exp(term->right, ctx); if (result) { if (term->left->type->tag != T_ARROW) { type_error1(term->info, "expected function in application but found %s", term->left->type); result = 0; }; } if (result) { result = expect_types(term->info, term->right->type, 1, term->left->type->dom) != NULL; if (! result) { type_error_binop(term->info, "application", term->left->type, term->right->type); result = 0; } } if (result) term->type = ref(term->left->type->img); break; case A_VALUE: result = check_value(term); break; case A_IDENT: { struct type *t = ctx_lookup_type(term->info, ctx, term->ident); if (t == NULL) { syntax_error(term->info, "Undefined variable %s", term->ident->str); result = 0; } else { term->type = ref(t); } } break; case A_BRACKET: result = check_exp(term->brexp, ctx); if (result) { term->type = ref(expect_types(term->info, term->brexp->type, 1, t_lens)); if (term->type == NULL) { type_error1(term->info, "[..] is only defined for lenses, not for %s", term->brexp->type); result = 0; } } break; case A_FUNC: { bind_param(&ctx->local, term->param, NULL); result = check_exp(term->body, ctx); if (result) { term->type = make_arrow_type(term->param->type, term->body->type); } unbind_param(&ctx->local, term->param); } break; case A_REP: result = check_exp(term->exp, ctx); if (result) { term->type = ref(expect_types(term->info, term->exp->type, 2, t_regexp, t_lens)); if (term->type == NULL) { type_error1(term->info, "Incompatible types: repetition is only defined" " for regexp and lens, not for %s", term->exp->type); result = 0; } } break; default: assert(0); break; } assert(!result || term->type != NULL); return result; } static int check_decl(struct term *term, struct ctx *ctx) { assert(term->tag == A_BIND || term->tag == A_TEST); if (term->tag == A_BIND) { if (!check_exp(term->exp, ctx)) return 0; term->type = ref(term->exp->type); if (bnd_lookup(ctx->local, term->bname) != NULL) { syntax_error(term->info, "the name %s is already defined", term->bname); return 0; } bind_type(&ctx->local, term->bname, term->type); } else if (term->tag == A_TEST) { if (!check_exp(term->test, ctx)) return 0; if (term->result != NULL) { if (!check_exp(term->result, ctx)) return 0; if (! type_equal(term->test->type, term->result->type)) { type_error2(term->info, "expected test result of type %s but got %s", term->result->type, term->test->type); return 0; } } else { if (expect_types(term->info, term->test->type, 2, t_string, t_tree) == NULL) return 0; } term->type = ref(term->test->type); } else { assert(0); } return 1; } static int typecheck(struct term *term, struct augeas *aug) { int ok = 1; struct ctx ctx; char *fname; const char *basenam; assert(term->tag == A_MODULE); /* Check that the module name is consistent with the filename */ fname = module_basename(term->mname); basenam = strrchr(term->info->filename->str, SEP); if (basenam == NULL) basenam = term->info->filename->str; else basenam += 1; if (STRNEQ(fname, basenam)) { syntax_error(term->info, "The module %s must be in a file named %s", term->mname, fname); free(fname); return 0; } free(fname); ctx.aug = aug; ctx.local = NULL; ctx.name = term->mname; list_for_each(dcl, term->decls) { ok &= check_decl(dcl, &ctx); } unref(ctx.local, binding); return ok; } static struct value *compile_exp(struct info *, struct term *, struct ctx *); static struct value *compile_union(struct term *exp, struct ctx *ctx) { struct value *v1 = compile_exp(exp->info, exp->left, ctx); if (EXN(v1)) return v1; struct value *v2 = compile_exp(exp->info, exp->right, ctx); if (EXN(v2)) { unref(v1, value); return v2; } struct type *t = exp->type; struct info *info = exp->info; struct value *v = NULL; v1 = coerce(v1, t); if (EXN(v1)) return v1; v2 = coerce(v2, t); if (EXN(v2)) { unref(v1, value); return v2; } if (t->tag == T_REGEXP) { v = make_value(V_REGEXP, ref(info)); v->regexp = regexp_union(info, v1->regexp, v2->regexp); } else if (t->tag == T_LENS) { struct lens *l1 = v1->lens; struct lens *l2 = v2->lens; v = lns_make_union(ref(info), ref(l1), ref(l2), LNS_TYPE_CHECK(ctx)); } else { fatal_error(info, "Tried to union a %s and a %s to yield a %s", type_name(exp->left->type), type_name(exp->right->type), type_name(t)); } unref(v1, value); unref(v2, value); return v; } static struct value *compile_minus(struct term *exp, struct ctx *ctx) { struct value *v1 = compile_exp(exp->info, exp->left, ctx); if (EXN(v1)) return v1; struct value *v2 = compile_exp(exp->info, exp->right, ctx); if (EXN(v2)) { unref(v1, value); return v2; } struct type *t = exp->type; struct info *info = exp->info; struct value *v; v1 = coerce(v1, t); v2 = coerce(v2, t); if (t->tag == T_REGEXP) { struct regexp *re1 = v1->regexp; struct regexp *re2 = v2->regexp; struct regexp *re = regexp_minus(info, re1, re2); if (re == NULL) { v = make_exn_value(ref(info), "Regular expression subtraction 'r1 - r2' failed"); exn_printf_line(v, "r1: /%s/", re1->pattern->str); exn_printf_line(v, "r2: /%s/", re2->pattern->str); } else { v = make_value(V_REGEXP, ref(info)); v->regexp = re; } } else { v = NULL; fatal_error(info, "Tried to subtract a %s and a %s to yield a %s", type_name(exp->left->type), type_name(exp->right->type), type_name(t)); } unref(v1, value); unref(v2, value); return v; } static struct value *compile_compose(struct term *exp, struct ctx *ctx) { struct info *info = exp->info; struct value *v; if (exp->left->type->tag == T_ARROW) { // FIXME: This is really crufty, and should be desugared in the // parser so that we don't have to do all this manual type // computation. Should we write function compostion as // concatenation instead of using a separate syntax ? /* Build lambda x: exp->right (exp->left x) as a closure */ char *var = strdup("@0"); struct term *func = make_param(var, ref(exp->left->type->dom), ref(info)); func->type = make_arrow_type(exp->left->type->dom, exp->right->type->img); struct term *ident = make_term(A_IDENT, ref(info)); ident->ident = ref(func->param->name); ident->type = ref(func->param->type); struct term *app = make_app_term(ref(exp->left), ident, ref(info)); app->type = ref(app->left->type->img); app = make_app_term(ref(exp->right), app, ref(info)); app->type = ref(app->right->type->img); build_func(func, app); if (!type_equal(func->type, exp->type)) { char *f = type_string(func->type); char *e = type_string(exp->type); fatal_error(info, "Composition has type %s but should have type %s", f, e); free(f); free(e); unref(func, term); return info->error->exn; } v = make_closure(func, ctx->local); unref(func, term); } else { v = compile_exp(exp->info, exp->left, ctx); unref(v, value); v = compile_exp(exp->info, exp->right, ctx); } return v; } static struct value *compile_concat(struct term *exp, struct ctx *ctx) { struct value *v1 = compile_exp(exp->info, exp->left, ctx); if (EXN(v1)) return v1; struct value *v2 = compile_exp(exp->info, exp->right, ctx); if (EXN(v2)) { unref(v1, value); return v2; } struct type *t = exp->type; struct info *info = exp->info; struct value *v; v1 = coerce(v1, t); v2 = coerce(v2, t); if (t->tag == T_STRING) { const char *s1 = v1->string->str; const char *s2 = v2->string->str; v = make_value(V_STRING, ref(info)); make_ref(v->string); if (ALLOC_N(v->string->str, strlen(s1) + strlen(s2) + 1) < 0) goto error; char *s = v->string->str; strcpy(s, s1); strcat(s, s2); } else if (t->tag == T_REGEXP) { v = make_value(V_REGEXP, ref(info)); v->regexp = regexp_concat(info, v1->regexp, v2->regexp); } else if (t->tag == T_FILTER) { struct filter *f1 = v1->filter; struct filter *f2 = v2->filter; v = make_value(V_FILTER, ref(info)); if (v2->ref == 1 && f2->ref == 1) { list_append(f2, ref(f1)); v->filter = ref(f2); } else if (v1->ref == 1 && f1->ref == 1) { list_append(f1, ref(f2)); v->filter = ref(f1); } else { struct filter *cf1, *cf2; cf1 = make_filter(ref(f1->glob), f1->include); cf2 = make_filter(ref(f2->glob), f2->include); cf1->next = ref(f1->next); cf2->next = ref(f2->next); list_append(cf1, cf2); v->filter = cf1; } } else if (t->tag == T_LENS) { struct lens *l1 = v1->lens; struct lens *l2 = v2->lens; v = lns_make_concat(ref(info), ref(l1), ref(l2), LNS_TYPE_CHECK(ctx)); } else { v = NULL; fatal_error(info, "Tried to concat a %s and a %s to yield a %s", type_name(exp->left->type), type_name(exp->right->type), type_name(t)); } unref(v1, value); unref(v2, value); return v; error: return exp->info->error->exn; } static struct value *apply(struct term *app, struct ctx *ctx) { struct value *f = compile_exp(app->info, app->left, ctx); struct value *result = NULL; struct ctx lctx; if (EXN(f)) return f; struct value *arg = compile_exp(app->info, app->right, ctx); if (EXN(arg)) { unref(f, value); return arg; } assert(f->tag == V_CLOS); lctx.aug = ctx->aug; lctx.local = ref(f->bindings); lctx.name = ctx->name; arg = coerce(arg, f->func->param->type); if (arg == NULL) goto done; bind_param(&lctx.local, f->func->param, arg); result = compile_exp(app->info, f->func->body, &lctx); unref(result->info, info); result->info = ref(app->info); unbind_param(&lctx.local, f->func->param); done: unref(lctx.local, binding); unref(arg, value); unref(f, value); return result; } static struct value *compile_bracket(struct term *exp, struct ctx *ctx) { struct value *arg = compile_exp(exp->info, exp->brexp, ctx); if (EXN(arg)) return arg; assert(arg->tag == V_LENS); struct value *v = lns_make_subtree(ref(exp->info), ref(arg->lens)); unref(arg, value); return v; } static struct value *compile_rep(struct term *rep, struct ctx *ctx) { struct value *arg = compile_exp(rep->info, rep->rexp, ctx); struct value *v = NULL; if (EXN(arg)) return arg; arg = coerce(arg, rep->type); if (rep->type->tag == T_REGEXP) { int min, max; if (rep->quant == Q_STAR) { min = 0; max = -1; } else if (rep->quant == Q_PLUS) { min = 1; max = -1; } else if (rep->quant == Q_MAYBE) { min = 0; max = 1; } else { assert(0); abort(); } v = make_value(V_REGEXP, ref(rep->info)); v->regexp = regexp_iter(rep->info, arg->regexp, min, max); } else if (rep->type->tag == T_LENS) { int c = LNS_TYPE_CHECK(ctx); if (rep->quant == Q_STAR) { v = lns_make_star(ref(rep->info), ref(arg->lens), c); } else if (rep->quant == Q_PLUS) { v = lns_make_plus(ref(rep->info), ref(arg->lens), c); } else if (rep->quant == Q_MAYBE) { v = lns_make_maybe(ref(rep->info), ref(arg->lens), c); } else { assert(0); } } else { fatal_error(rep->info, "Tried to repeat a %s to yield a %s", type_name(rep->rexp->type), type_name(rep->type)); } unref(arg, value); return v; } static struct value *compile_exp(struct info *info, struct term *exp, struct ctx *ctx) { struct value *v = NULL; switch (exp->tag) { case A_COMPOSE: v = compile_compose(exp, ctx); break; case A_UNION: v = compile_union(exp, ctx); break; case A_MINUS: v = compile_minus(exp, ctx); break; case A_CONCAT: v = compile_concat(exp, ctx); break; case A_APP: v = apply(exp, ctx); break; case A_VALUE: if (exp->value->tag == V_NATIVE) { v = native_call(info, exp->value->native, ctx); } else { v = ref(exp->value); } break; case A_IDENT: v = ref(ctx_lookup(exp->info, ctx, exp->ident)); break; case A_BRACKET: v = compile_bracket(exp, ctx); break; case A_FUNC: v = make_closure(exp, ctx->local); break; case A_REP: v = compile_rep(exp, ctx); break; default: assert(0); break; } return v; } static int compile_test(struct term *term, struct ctx *ctx) { struct value *actual = compile_exp(term->info, term->test, ctx); struct value *expect = NULL; int ret = 1; if (term->tr_tag == TR_EXN) { if (!EXN(actual)) { print_info(stdout, term->info); printf("Test run should have produced exception, but produced\n"); print_value(stdout, actual); printf("\n"); ret = 0; } } else { if (EXN(actual)) { print_info(stdout, term->info); printf("exception thrown in test\n"); print_value(stdout, actual); printf("\n"); ret = 0; } else if (term->tr_tag == TR_CHECK) { expect = compile_exp(term->info, term->result, ctx); if (EXN(expect)) goto done; if (! value_equal(actual, expect)) { printf("Test failure:"); print_info(stdout, term->info); printf("\n"); printf(" Expected:\n"); print_value(stdout, expect); printf("\n"); printf(" Actual:\n"); print_value(stdout, actual); printf("\n"); ret = 0; } } else { printf("Test result: "); print_info(stdout, term->info); printf("\n"); if (actual->tag == V_TREE) { print_tree_braces(stdout, 2, actual->origin->children); } else { print_value(stdout, actual); } printf("\n"); } } done: reset_error(term->info->error); unref(actual, value); unref(expect, value); return ret; } static int compile_decl(struct term *term, struct ctx *ctx) { if (term->tag == A_BIND) { int result; struct value *v = compile_exp(term->info, term->exp, ctx); bind(&ctx->local, term->bname, term->type, v); if (EXN(v) && !v->exn->seen) { struct error *error = term->info->error; struct memstream ms; init_memstream(&ms); syntax_error(term->info, "Failed to compile %s", term->bname); fprintf(ms.stream, "%s\n", error->details); print_value(ms.stream, v); close_memstream(&ms); v->exn->seen = 1; free(error->details); error->details = ms.buf; } result = !(EXN(v) || HAS_ERR(ctx->aug)); unref(v, value); return result; } else if (term->tag == A_TEST) { return compile_test(term, ctx); } assert(0); abort(); } static struct module *compile(struct term *term, struct augeas *aug) { struct ctx ctx; struct transform *autoload = NULL; assert(term->tag == A_MODULE); ctx.aug = aug; ctx.local = NULL; ctx.name = term->mname; list_for_each(dcl, term->decls) { if (!compile_decl(dcl, &ctx)) goto error; } if (term->autoload != NULL) { struct binding *bnd = bnd_lookup(ctx.local, term->autoload); if (bnd == NULL) { syntax_error(term->info, "Undefined transform in autoload %s", term->autoload); goto error; } if (expect_types(term->info, bnd->type, 1, t_transform) == NULL) goto error; autoload = bnd->value->transform; } struct module *module = module_create(term->mname); module->bindings = ctx.local; module->autoload = ref(autoload); return module; error: unref(ctx.local, binding); return NULL; } /* * Defining native functions */ static struct info * make_native_info(struct error *error, const char *fname, int line) { struct info *info; if (make_ref(info) < 0) goto error; info->first_line = info->last_line = line; info->first_column = info->last_column = 0; info->error = error; if (make_ref(info->filename) < 0) goto error; info->filename->str = strdup(fname); return info; error: unref(info, info); return NULL; } int define_native_intl(const char *file, int line, struct error *error, struct module *module, const char *name, int argc, func_impl impl, ...) { assert(argc > 0); /* We have no unit type */ assert(argc <= 5); va_list ap; enum type_tag tag; struct term *params = NULL, *body = NULL, *func = NULL; struct type *type; struct value *v = NULL; struct info *info = NULL; struct ctx ctx; info = make_native_info(error, file, line); if (info == NULL) goto error; va_start(ap, impl); for (int i=0; i < argc; i++) { struct term *pterm; char ident[10]; tag = va_arg(ap, enum type_tag); type = make_base_type(tag); snprintf(ident, 10, "@%d", i); pterm = make_param(strdup(ident), type, ref(info)); list_append(params, pterm); } tag = va_arg(ap, enum type_tag); va_end(ap); type = make_base_type(tag); make_ref(v); if (v == NULL) goto error; v->tag = V_NATIVE; v->info = info; info = NULL; if (ALLOC(v->native) < 0) goto error; v->native->argc = argc; v->native->type = type; v->native->impl = impl; make_ref(body); if (body == NULL) goto error; body->info = ref(info); body->type = ref(type); body->tag = A_VALUE; body->value = v; v = NULL; func = build_func(params, body); params = NULL; body = NULL; ctx.aug = NULL; ctx.local = ref(module->bindings); ctx.name = module->name; if (! check_exp(func, &ctx)) { fatal_error(info, "Typechecking native %s failed", name); abort(); } v = make_closure(func, ctx.local); if (v == NULL) { unref(module->bindings, binding); goto error; } bind(&ctx.local, name, func->type, v); unref(v, value); unref(func, term); unref(module->bindings, binding); module->bindings = ctx.local; return 0; error: list_for_each(p, params) { unref(p, term); } unref(v, value); unref(body, term); unref(func, term); return -1; } /* Defined in parser.y */ int augl_parse_file(struct augeas *aug, const char *name, struct term **term); static char *module_basename(const char *modname) { char *fname; if (asprintf(&fname, "%s" AUG_EXT, modname) == -1) return NULL; for (int i=0; i < strlen(modname); i++) fname[i] = tolower(fname[i]); return fname; } static char *module_filename(struct augeas *aug, const char *modname) { char *dir = NULL; char *filename = NULL; char *name = module_basename(modname); /* Module names that contain slashes can fool us into finding and * loading a module in another directory, but once loaded we won't find * it under MODNAME so that we will later try and load it over and * over */ if (index(modname, '/') != NULL) goto error; while ((dir = argz_next(aug->modpathz, aug->nmodpath, dir)) != NULL) { int len = strlen(name) + strlen(dir) + 2; struct stat st; if (REALLOC_N(filename, len) == -1) goto error; sprintf(filename, "%s/%s", dir, name); if (stat(filename, &st) == 0) goto done; } error: FREE(filename); done: free(name); return filename; } int load_module_file(struct augeas *aug, const char *filename, const char *name) { struct term *term = NULL; int result = -1; if (aug->flags & AUG_TRACE_MODULE_LOADING) printf("Module %s", filename); augl_parse_file(aug, filename, &term); if (aug->flags & AUG_TRACE_MODULE_LOADING) printf(HAS_ERR(aug) ? " failed\n" : " loaded\n"); ERR_BAIL(aug); if (! typecheck(term, aug)) goto error; struct module *module = compile(term, aug); bool bad_module = (module == NULL); if (bad_module && name != NULL) { /* Put an empty placeholder on the module list so that * we don't retry loading this module everytime its mentioned */ module = module_create(name); } if (module != NULL) { list_append(aug->modules, module); list_for_each(bnd, module->bindings) { if (bnd->value->tag == V_LENS) { lens_release(bnd->value->lens); } } } ERR_THROW(bad_module, aug, AUG_ESYNTAX, "Failed to load %s", filename); result = 0; error: // FIXME: This leads to a bad free of a string used in a del lens // To reproduce run lenses/tests/test_yum.aug unref(term, term); return result; } static int load_module(struct augeas *aug, const char *name) { char *filename = NULL; if (module_find(aug->modules, name) != NULL) return 0; if ((filename = module_filename(aug, name)) == NULL) return -1; if (load_module_file(aug, filename, name) == -1) goto error; free(filename); return 0; error: free(filename); return -1; } int interpreter_init(struct augeas *aug) { int r; r = init_fatal_exn(aug->error); if (r < 0) return -1; aug->modules = builtin_init(aug->error); if (aug->flags & AUG_NO_MODL_AUTOLOAD) return 0; // For now, we just load every file on the search path const char *dir = NULL; glob_t globbuf; int gl_flags = GLOB_NOSORT; MEMZERO(&globbuf, 1); while ((dir = argz_next(aug->modpathz, aug->nmodpath, dir)) != NULL) { char *globpat; r = asprintf(&globpat, "%s/*.aug", dir); ERR_NOMEM(r < 0, aug); r = glob(globpat, gl_flags, NULL, &globbuf); if (r != 0 && r != GLOB_NOMATCH) { /* This really has to be an allocation failure; glob is not * supposed to return GLOB_ABORTED here */ aug_errcode_t code = r == GLOB_NOSPACE ? AUG_ENOMEM : AUG_EINTERNAL; ERR_REPORT(aug, code, "glob failure for %s", globpat); free(globpat); goto error; } gl_flags |= GLOB_APPEND; free(globpat); } for (int i=0; i < globbuf.gl_pathc; i++) { char *name, *p, *q; int res; p = strrchr(globbuf.gl_pathv[i], SEP); if (p == NULL) p = globbuf.gl_pathv[i]; else p += 1; q = strchr(p, '.'); name = strndup(p, q - p); name[0] = toupper(name[0]); res = load_module(aug, name); free(name); if (res == -1) goto error; } globfree(&globbuf); return 0; error: globfree(&globbuf); return -1; } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/builtin.c0000644000175000017500000005307014161102026012361 00000000000000/* * builtin.c: builtin primitives * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include #include #include #include #include "syntax.h" #include "memory.h" #include "transform.h" #include "errcode.h" #define UNIMPL_BODY(name) \ { \ FIXME(#name " called"); \ abort(); \ } /* * Lenses */ /* V_REGEXP -> V_STRING -> V_LENS */ static struct value *lns_del(struct info *info, struct value **argv) { struct value *rxp = argv[0]; struct value *dflt = argv[1]; assert(rxp->tag == V_REGEXP); assert(dflt->tag == V_STRING); return lns_make_prim(L_DEL, ref(info), ref(rxp->regexp), ref(dflt->string)); } /* V_REGEXP -> V_LENS */ static struct value *lns_store(struct info *info, struct value **argv) { struct value *rxp = argv[0]; assert(rxp->tag == V_REGEXP); return lns_make_prim(L_STORE, ref(info), ref(rxp->regexp), NULL); } /* V_STRING -> V_LENS */ static struct value *lns_value(struct info *info, struct value **argv) { struct value *str = argv[0]; assert(str->tag == V_STRING); return lns_make_prim(L_VALUE, ref(info), NULL, ref(str->string)); } /* V_REGEXP -> V_LENS */ static struct value *lns_key(struct info *info, struct value **argv) { struct value *rxp = argv[0]; assert(rxp->tag == V_REGEXP); return lns_make_prim(L_KEY, ref(info), ref(rxp->regexp), NULL); } /* V_STRING -> V_LENS */ static struct value *lns_label(struct info *info, struct value **argv) { struct value *str = argv[0]; assert(str->tag == V_STRING); return lns_make_prim(L_LABEL, ref(info), NULL, ref(str->string)); } /* V_STRING -> V_LENS */ static struct value *lns_seq(struct info *info, struct value **argv) { struct value *str = argv[0]; assert(str->tag == V_STRING); return lns_make_prim(L_SEQ, ref(info), NULL, ref(str->string)); } /* V_STRING -> V_LENS */ static struct value *lns_counter(struct info *info, struct value **argv) { struct value *str = argv[0]; assert(str->tag == V_STRING); return lns_make_prim(L_COUNTER, ref(info), NULL, ref(str->string)); } /* V_LENS -> V_LENS -> V_LENS -> V_LENS */ static struct value *lns_square(struct info *info, struct value **argv) { struct value *l1 = argv[0]; struct value *l2 = argv[1]; struct value *l3 = argv[2]; assert(l1->tag == V_LENS); assert(l2->tag == V_LENS); assert(l3->tag == V_LENS); int check = typecheck_p(info); return lns_make_square(ref(info), ref(l1->lens), ref(l2->lens), ref(l3->lens), check); } static void exn_lns_error_detail(struct value *exn, const char *label, struct lens *lens) { if (lens == NULL) return; char *s = format_info(lens->info); exn_printf_line(exn, "%s: %s", label, s); free(s); } static struct value *make_exn_lns_error(struct info *info, struct lns_error *err, const char *text) { struct value *v; if (HAS_ERR(info)) return info->error->exn; v = make_exn_value(ref(info), "%s", err->message); exn_lns_error_detail(v, "Lens", err->lens); exn_lns_error_detail(v, " Last match", err->last); exn_lns_error_detail(v, " Not matching", err->next); if (err->pos >= 0) { char *pos = format_pos(text, err->pos); size_t line, ofs; calc_line_ofs(text, err->pos, &line, &ofs); exn_printf_line(v, "Error encountered at %d:%d (%d characters into string)", (int) line, (int) ofs, err->pos); if (pos != NULL) exn_printf_line(v, "%s", pos); free(pos); } else { exn_printf_line(v, "Error encountered at path %s", err->path); } return v; } static void exn_print_tree(struct value *exn, struct tree *tree) { struct memstream ms; init_memstream(&ms); dump_tree(ms.stream, tree); close_memstream(&ms); exn_printf_line(exn, "%s", ms.buf); FREE(ms.buf); } static struct value *make_pathx_exn(struct info *info, struct pathx *p) { struct value *v; char *msg; const char *txt, *px_err; int pos; px_err = pathx_error(p, &txt, &pos); v = make_exn_value(ref(info), "syntax error in path expression: %s", px_err); if (ALLOC_N(msg, strlen(txt) + 4) >= 0) { strncpy(msg, txt, pos); strcat(msg, "|=|"); strcat(msg, txt + pos); exn_add_lines(v, 1, msg); } return v; } static struct value *pathx_parse_glue(struct info *info, struct value *tree, struct value *path, struct pathx **p) { assert(path->tag == V_STRING); assert(tree->tag == V_TREE); if (pathx_parse(tree->origin, info->error, path->string->str, true, NULL, NULL, p) != PATHX_NOERROR) { return make_pathx_exn(info, *p); } else { return NULL; } } /* V_LENS -> V_STRING -> V_TREE */ static struct value *lens_get(struct info *info, struct value **argv) { struct value *l = argv[0]; struct value *str = argv[1]; assert(l->tag == V_LENS); assert(str->tag == V_STRING); struct lns_error *err; struct value *v; const char *text = str->string->str; struct tree *tree = lns_get(info, l->lens, text, 0, &err); if (err == NULL && ! HAS_ERR(info)) { v = make_value(V_TREE, ref(info)); v->origin = make_tree_origin(tree); } else { struct tree *t = make_tree_origin(tree); if (t == NULL) free_tree(tree); tree = t; v = make_exn_lns_error(info, err, text); if (tree != NULL) { exn_printf_line(v, "Tree generated so far:"); exn_print_tree(v, tree); free_tree(tree); } free_lns_error(err); } return v; } /* V_LENS -> V_TREE -> V_STRING -> V_STRING */ static struct value *lens_put(struct info *info, struct value **argv) { struct value *l = argv[0]; struct value *tree = argv[1]; struct value *str = argv[2]; assert(l->tag == V_LENS); assert(tree->tag == V_TREE); assert(str->tag == V_STRING); struct memstream ms; struct value *v; struct lns_error *err; init_memstream(&ms); lns_put(info, ms.stream, l->lens, tree->origin->children, str->string->str, 0, &err); close_memstream(&ms); if (err == NULL && ! HAS_ERR(info)) { v = make_value(V_STRING, ref(info)); v->string = make_string(ms.buf); } else { v = make_exn_lns_error(info, err, str->string->str); free_lns_error(err); FREE(ms.buf); } return v; } /* V_STRING -> V_STRING -> V_TREE -> V_TREE */ static struct value *tree_set_glue(struct info *info, struct value **argv) { // FIXME: This only works if TREE is not referenced more than once; // otherwise we'll have some pretty weird semantics, and would really // need to copy TREE first struct value *path = argv[0]; struct value *val = argv[1]; struct value *tree = argv[2]; assert(path->tag == V_STRING); assert(val->tag == V_STRING); assert(tree->tag == V_TREE); struct tree *fake = NULL; struct pathx *p = NULL; struct value *result = NULL; if (tree->origin->children == NULL) { tree->origin->children = make_tree(NULL, NULL, tree->origin, NULL); fake = tree->origin->children; } result = pathx_parse_glue(info, tree, path, &p); if (result != NULL) goto done; if (tree_set(p, val->string->str) == NULL) { result = make_exn_value(ref(info), "Tree set of %s to '%s' failed", path->string->str, val->string->str); goto done; } if (fake != NULL) { list_remove(fake, tree->origin->children); free_tree(fake); } result = ref(tree); done: free_pathx(p); return result; } /* V_STRING -> V_TREE -> V_TREE */ static struct value *tree_clear_glue(struct info *info, struct value **argv) { // FIXME: This only works if TREE is not referenced more than once; // otherwise we'll have some pretty weird semantics, and would really // need to copy TREE first struct value *path = argv[0]; struct value *tree = argv[1]; assert(path->tag == V_STRING); assert(tree->tag == V_TREE); struct tree *fake = NULL; struct pathx *p = NULL; struct value *result = NULL; if (tree->origin->children == NULL) { tree->origin->children = make_tree(NULL, NULL, tree->origin, NULL); fake = tree->origin->children; } result = pathx_parse_glue(info, tree, path, &p); if (result != NULL) goto done; if (tree_set(p, NULL) == NULL) { result = make_exn_value(ref(info), "Tree set of %s to NULL failed", path->string->str); goto done; } if (fake != NULL) { list_remove(fake, tree->origin->children); free_tree(fake); } result = ref(tree); done: free_pathx(p); return result; } static struct value *tree_insert_glue(struct info *info, struct value *label, struct value *path, struct value *tree, int before) { // FIXME: This only works if TREE is not referenced more than once; // otherwise we'll have some pretty weird semantics, and would really // need to copy TREE first assert(label->tag == V_STRING); assert(path->tag == V_STRING); assert(tree->tag == V_TREE); int r; struct pathx *p = NULL; struct value *result = NULL; result = pathx_parse_glue(info, tree, path, &p); if (result != NULL) goto done; r = tree_insert(p, label->string->str, before); if (r != 0) { result = make_exn_value(ref(info), "Tree insert of %s at %s failed", label->string->str, path->string->str); goto done; } result = ref(tree); done: free_pathx(p); return result; } /* Insert after */ /* V_STRING -> V_STRING -> V_TREE -> V_TREE */ static struct value *tree_insa_glue(struct info *info, struct value **argv) { struct value *label = argv[0]; struct value *path = argv[1]; struct value *tree = argv[2]; return tree_insert_glue(info, label, path, tree, 0); } /* Insert before */ /* V_STRING -> V_STRING -> V_TREE -> V_TREE */ static struct value *tree_insb_glue(struct info *info, struct value **argv) { struct value *label = argv[0]; struct value *path = argv[1]; struct value *tree = argv[2]; return tree_insert_glue(info, label, path, tree, 1); } /* V_STRING -> V_TREE -> V_TREE */ static struct value *tree_rm_glue(struct info *info, struct value **argv) { // FIXME: This only works if TREE is not referenced more than once; // otherwise we'll have some pretty weird semantics, and would really // need to copy TREE first struct value *path = argv[0]; struct value *tree = argv[1]; assert(path->tag == V_STRING); assert(tree->tag == V_TREE); struct pathx *p = NULL; struct value *result = NULL; result = pathx_parse_glue(info, tree, path, &p); if (result != NULL) goto done; if (tree_rm(p) == -1) { result = make_exn_value(ref(info), "Tree rm of %s failed", path->string->str); goto done; } result = ref(tree); done: free_pathx(p); return result; } /* V_STRING -> V_STRING */ static struct value *gensym(struct info *info, struct value **argv) { struct value *prefix = argv[0]; assert(prefix->tag == V_STRING); static unsigned int count = 0; struct value *v; char *s; int r; r = asprintf(&s, "%s%u", prefix->string->str, count); if (r == -1) return NULL; v = make_value(V_STRING, ref(info)); v->string = make_string(s); return v; } /* V_STRING -> V_FILTER */ static struct value *xform_incl(struct info *info, struct value **argv) { struct value *s = argv[0]; assert(s->tag == V_STRING); struct value *v = make_value(V_FILTER, ref(info)); v->filter = make_filter(ref(s->string), 1); return v; } /* V_STRING -> V_FILTER */ static struct value *xform_excl(struct info *info, struct value **argv) { struct value *s = argv[0]; assert(s->tag == V_STRING); struct value *v = make_value(V_FILTER, ref(info)); v->filter = make_filter(ref(s->string), 0); return v; } /* V_LENS -> V_FILTER -> V_TRANSFORM */ static struct value *xform_transform(struct info *info, struct value **argv) { struct value *l = argv[0]; struct value *f = argv[1]; assert(l->tag == V_LENS); assert(f->tag == V_FILTER); if (l->lens->value || l->lens->key) { return make_exn_value(ref(info), "Can not build a transform " "from a lens that leaves a %s behind", l->lens->key ? "key" : "value"); } struct value *v = make_value(V_TRANSFORM, ref(info)); v->transform = make_transform(ref(l->lens), ref(f->filter)); return v; } static struct value *sys_getenv(struct info *info, struct value **argv) { assert(argv[0]->tag == V_STRING); struct value *v = make_value(V_STRING, ref(info)); v->string = dup_string(getenv(argv[0]->string->str)); return v; } static struct value *sys_read_file(struct info *info, struct value **argv) { struct value *n = argv[0]; assert(n->tag == V_STRING); char *str = NULL; str = xread_file(n->string->str); if (str == NULL) { char error_buf[1024]; const char *errmsg; errmsg = xstrerror(errno, error_buf, sizeof(error_buf)); struct value *exn = make_exn_value(ref(info), "reading file %s failed:", n->string->str); exn_printf_line(exn, "%s", errmsg); return exn; } struct value *v = make_value(V_STRING, ref(info)); v->string = make_string(str); return v; } /* V_LENS -> V_LENS */ static struct value *lns_check_rec_glue(struct info *info, struct value **argv) { struct value *l = argv[0]; struct value *r = argv[1]; assert(l->tag == V_LENS); assert(r->tag == V_LENS); int check = typecheck_p(info); return lns_check_rec(info, l->lens, r->lens, check); } /* * Print functions */ /* V_STRING -> V_UNIT */ static struct value *pr_string(struct info *info, struct value **argv) { printf("%s", argv[0]->string->str); return make_unit(ref(info)); } /* V_REGEXP -> V_UNIT */ static struct value *pr_regexp(struct info *info, struct value **argv) { print_regexp(stdout, argv[0]->regexp); return make_unit(ref(info)); } /* V_STRING -> V_UNIT */ static struct value *pr_endline(struct info *info, struct value **argv) { printf("%s\n", argv[0]->string->str); return make_unit(ref(info)); } /* V_TREE -> V_TREE */ static struct value *pr_tree(ATTRIBUTE_UNUSED struct info *info, struct value **argv) { print_tree_braces(stdout, 0, argv[0]->origin); return ref(argv[0]); } /* * Lens inspection */ static struct value *lns_value_of_type(struct info *info, struct regexp *rx) { struct value *result = make_value(V_REGEXP, ref(info)); if (rx) result->regexp = ref(rx); else result->regexp = regexp_make_empty(ref(info)); return result; } /* V_LENS -> V_REGEXP */ static struct value *lns_ctype(struct info *info, struct value **argv) { return lns_value_of_type(info, argv[0]->lens->ctype); } /* V_LENS -> V_REGEXP */ static struct value *lns_atype(struct info *info, struct value **argv) { return lns_value_of_type(info, argv[0]->lens->atype); } /* V_LENS -> V_REGEXP */ static struct value *lns_vtype(struct info *info, struct value **argv) { return lns_value_of_type(info, argv[0]->lens->vtype); } /* V_LENS -> V_REGEXP */ static struct value *lns_ktype(struct info *info, struct value **argv) { return lns_value_of_type(info, argv[0]->lens->ktype); } /* V_LENS -> V_STRING */ static struct value *lns_fmt_atype(struct info *info, struct value **argv) { struct value *l = argv[0]; struct value *result = NULL; char *s = NULL; int r; r = lns_format_atype(l->lens, &s); if (r < 0) return info->error->exn; result = make_value(V_STRING, ref(info)); result->string = make_string(s); return result; } /* V_REGEXP -> V_STRING -> V_STRING */ static struct value *rx_match(struct info *info, struct value **argv) { struct value *rx = argv[0]; struct value *s = argv[1]; struct value *result = NULL; const char *str = s->string->str; struct re_registers regs; int r; MEMZERO(®s, 1); r = regexp_match(rx->regexp, str, strlen(str), 0, ®s); if (r < -1) { result = make_exn_value(ref(info), "regexp match failed (internal error)"); } else { char *match = NULL; if (r == -1) { /* No match */ match = strdup(""); } else { match = strndup(str + regs.start[0], regs.end[0] - regs.start[0]); } if (match == NULL) { result = info->error->exn; } else { result = make_value(V_STRING, ref(info)); result->string = make_string(match); } } return result; } struct module *builtin_init(struct error *error) { struct module *modl = module_create("Builtin"); int r; #define DEFINE_NATIVE(modl, name, nargs, impl, types ...) \ r = define_native(error, modl, name, nargs, impl, ##types); \ if (r < 0) goto error; DEFINE_NATIVE(modl, "gensym", 1, gensym, T_STRING, T_STRING); /* Primitive lenses */ DEFINE_NATIVE(modl, "del", 2, lns_del, T_REGEXP, T_STRING, T_LENS); DEFINE_NATIVE(modl, "store", 1, lns_store, T_REGEXP, T_LENS); DEFINE_NATIVE(modl, "value", 1, lns_value, T_STRING, T_LENS); DEFINE_NATIVE(modl, "key", 1, lns_key, T_REGEXP, T_LENS); DEFINE_NATIVE(modl, "label", 1, lns_label, T_STRING, T_LENS); DEFINE_NATIVE(modl, "seq", 1, lns_seq, T_STRING, T_LENS); DEFINE_NATIVE(modl, "counter", 1, lns_counter, T_STRING, T_LENS); DEFINE_NATIVE(modl, "square", 3, lns_square, T_LENS, T_LENS, T_LENS, T_LENS); /* Applying lenses (mostly for tests) */ DEFINE_NATIVE(modl, "get", 2, lens_get, T_LENS, T_STRING, T_TREE); DEFINE_NATIVE(modl, "put", 3, lens_put, T_LENS, T_TREE, T_STRING, T_STRING); /* Tree manipulation used by the PUT tests */ DEFINE_NATIVE(modl, "set", 3, tree_set_glue, T_STRING, T_STRING, T_TREE, T_TREE); DEFINE_NATIVE(modl, "clear", 2, tree_clear_glue, T_STRING, T_TREE, T_TREE); DEFINE_NATIVE(modl, "rm", 2, tree_rm_glue, T_STRING, T_TREE, T_TREE); DEFINE_NATIVE(modl, "insa", 3, tree_insa_glue, T_STRING, T_STRING, T_TREE, T_TREE); DEFINE_NATIVE(modl, "insb", 3, tree_insb_glue, T_STRING, T_STRING, T_TREE, T_TREE); /* Transforms and filters */ DEFINE_NATIVE(modl, "incl", 1, xform_incl, T_STRING, T_FILTER); DEFINE_NATIVE(modl, "excl", 1, xform_excl, T_STRING, T_FILTER); DEFINE_NATIVE(modl, "transform", 2, xform_transform, T_LENS, T_FILTER, T_TRANSFORM); DEFINE_NATIVE(modl, LNS_CHECK_REC_NAME, 2, lns_check_rec_glue, T_LENS, T_LENS, T_LENS); /* Printing */ DEFINE_NATIVE(modl, "print_string", 1, pr_string, T_STRING, T_UNIT); DEFINE_NATIVE(modl, "print_regexp", 1, pr_regexp, T_REGEXP, T_UNIT); DEFINE_NATIVE(modl, "print_endline", 1, pr_endline, T_STRING, T_UNIT); DEFINE_NATIVE(modl, "print_tree", 1, pr_tree, T_TREE, T_TREE); /* Lens inspection */ DEFINE_NATIVE(modl, "lens_ctype", 1, lns_ctype, T_LENS, T_REGEXP); DEFINE_NATIVE(modl, "lens_atype", 1, lns_atype, T_LENS, T_REGEXP); DEFINE_NATIVE(modl, "lens_vtype", 1, lns_vtype, T_LENS, T_REGEXP); DEFINE_NATIVE(modl, "lens_ktype", 1, lns_ktype, T_LENS, T_REGEXP); DEFINE_NATIVE(modl, "lens_format_atype", 1, lns_fmt_atype, T_LENS, T_STRING); /* Regexp matching */ DEFINE_NATIVE(modl, "regexp_match", 2, rx_match, T_REGEXP, T_STRING, T_STRING); /* System functions */ struct module *sys = module_create("Sys"); modl->next = sys; DEFINE_NATIVE(sys, "getenv", 1, sys_getenv, T_STRING, T_STRING); DEFINE_NATIVE(sys, "read_file", 1, sys_read_file, T_STRING, T_STRING); return modl; error: unref(modl, module); return NULL; } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/errcode.c0000644000175000017500000000463314161102026012337 00000000000000/* * errcode.c: internal interface for error reporting * * Copyright (C) 2009-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include "errcode.h" #include "memory.h" #include static void vreport_error(struct error *err, aug_errcode_t errcode, const char *format, va_list ap) { /* We only remember the first error */ if (err->code != AUG_NOERROR) return; assert(err->details == NULL); err->code = errcode; if (format != NULL) { if (vasprintf(&err->details, format, ap) < 0) err->details = NULL; } } void report_error(struct error *err, aug_errcode_t errcode, const char *format, ...) { va_list ap; va_start(ap, format); vreport_error(err, errcode, format, ap); va_end(ap); } void bug_on(struct error *err, const char *srcfile, int srclineno, const char *format, ...) { char *msg = NULL; int r; va_list ap; if (err->code != AUG_NOERROR) return; va_start(ap, format); vreport_error(err, AUG_EINTERNAL, format, ap); va_end(ap); if (err->details == NULL) { xasprintf(&err->details, "%s:%d:internal error", srcfile, srclineno); } else { r = xasprintf(&msg, "%s:%d:%s", srcfile, srclineno, err->details); if (r >= 0) { free(err->details); err->details = msg; } } } void reset_error(struct error *err) { err->code = AUG_NOERROR; err->minor = 0; FREE(err->details); err->minor_details = NULL; } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/lens.h0000644000175000017500000002542414161102026011663 00000000000000/* * lens.h: Repreentation of lenses * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #ifndef LENS_H_ #define LENS_H_ #include "syntax.h" #include "fa.h" #include "jmt.h" /* keep in sync with tag name table */ enum lens_tag { L_DEL = 42, /* Shift tag values so we fail fast(er) on bad pointers */ L_STORE, L_VALUE, L_KEY, L_LABEL, L_SEQ, L_COUNTER, L_CONCAT, L_UNION, L_SUBTREE, L_STAR, L_MAYBE, L_REC, L_SQUARE }; /* A lens. The way the type information is computed is a little * delicate. There are various regexps involved to form the final type: * * CTYPE - the concrete type, used to parse file -> tree * ATYPE - the abstract type, used to parse tree -> file * KTYPE - the 'key' type, matching the label that this lens * can produce, or NULL if no label is produced * VTYPE - the 'value' type, matching the value that this lens * can produce, or NULL if no value is produce * * We distinguish between regular and recursive (context-free) lenses. Only * L_REC and the combinators can be marked recursive. * * Types are computed at different times, depending on whether the lens is * recursive or not. For non-recursive lenses, types are computed when the * lens is constructed by one of the LNS_MAKE_* functions; for recursive * lenses, we never compute an explicit ctype (since regular approximations * of it are pretty much useless), we do however compute regular * approximations of the ktype, vtype, and atype in LNS_CHECK_REC. That * means that recursive lenses accept context free languages in the string * -> tree direction, but only regular tree languages in the tree -> string * direction. * * Any lens that uses a recursive lens somehow is marked as recursive * itself. */ struct lens { unsigned int ref; enum lens_tag tag; struct info *info; struct regexp *ctype; /* NULL when recursive == 1 */ struct regexp *atype; struct regexp *ktype; struct regexp *vtype; struct jmt *jmt; /* When recursive == 1, might have jmt */ unsigned int value : 1; unsigned int key : 1; unsigned int recursive : 1; unsigned int consumes_value : 1; /* Whether we are inside a recursive lens or outside */ unsigned int rec_internal : 1; unsigned int ctype_nullable : 1; union { /* Primitive lenses */ struct { /* L_DEL uses both */ struct regexp *regexp; /* L_STORE, L_KEY */ struct string *string; /* L_VALUE, L_LABEL, L_SEQ, L_COUNTER */ }; /* Combinators */ struct lens *child; /* L_SUBTREE, L_STAR, L_MAYBE, L_SQUARE */ struct { /* L_UNION, L_CONCAT */ unsigned int nchildren; struct lens **children; }; struct { struct lens *body; /* L_REC */ /* We represent a recursive lens as two instances of struct * lens with L_REC. One has rec_internal set to 1, the other * has it set to 0. The one with rec_internal is used within * the body, the other is what is used from the 'outside'. This * is necessary to break the cycles inherent in recursive * lenses with reference counting. The link through alias is * set up in lns_check_rec, and not reference counted. * * Generally, any lens used in the body of a recursive lens is * marked with rec_internal == 1; lenses that use the recursive * lens 'from the outside' are marked with rec_internal == * 0. In the latter case, we can assign types right away, * except for the ctype, which we never have for any recursive * lens. */ struct lens *alias; }; }; }; /* Constructors for various lens types. Constructor assumes ownership of * arguments without incrementing. Caller owns returned lenses. * * The return type is VALUE instead of LENS so that we can return an * exception iftypechecking fails. */ struct value *lns_make_prim(enum lens_tag tag, struct info *info, struct regexp *regexp, struct string *string); struct value *lns_make_union(struct info *, struct lens *, struct lens *, int check); struct value *lns_make_concat(struct info *, struct lens *, struct lens *, int check); struct value *lns_make_subtree(struct info *, struct lens *); struct value *lns_make_star(struct info *, struct lens *, int check); struct value *lns_make_plus(struct info *, struct lens *, int check); struct value *lns_make_maybe(struct info *, struct lens *, int check); struct value *lns_make_square(struct info *, struct lens *, struct lens *, struct lens *lens, int check); /* Pretty-print a lens */ char *format_lens(struct lens *l); /* Pretty-print the atype of a lens. Allocates BUF, which must be freed by * the caller */ int lns_format_atype(struct lens *, char **buf); /* Recursive lenses */ struct value *lns_make_rec(struct info *info); struct value *lns_check_rec(struct info *info, struct lens *body, struct lens *rec, int check); /* Auxiliary data structures used during get/put/create */ struct skel { struct skel *next; enum lens_tag tag; union { char *text; /* L_DEL */ struct skel *skels; /* L_CONCAT, L_STAR, L_SQUARE */ }; /* Also tag == L_SUBTREE, with no data in the union */ }; struct lns_error { struct lens *lens; struct lens *last; /* The last lens that matched */ struct lens *next; /* The next lens that should match but doesn't */ int pos; /* Errors from get/parse */ char *path; /* Errors from put, pos will be -1 */ char *message; }; struct dict *make_dict(char *key, struct skel *skel, struct dict *subdict); void dict_lookup(const char *key, struct dict *dict, struct skel **skel, struct dict **subdict); int dict_append(struct dict **dict, struct dict *d2); void free_skel(struct skel *skel); void free_dict(struct dict *dict); void free_lns_error(struct lns_error *err); /* Parse text TEXT with LENS. INFO indicates where TEXT was read from. * * If ERR is non-NULL, *ERR is set to NULL on success, and to an error * message on failure; the constructed tree is always returned. If ERR is * NULL, return the tree on success, and NULL on failure. * * ENABLE_SPAN indicates whether span information should be collected or not */ struct tree *lns_get(struct info *info, struct lens *lens, const char *text, int enable_span, struct lns_error **err); struct skel *lns_parse(struct lens *lens, const char *text, struct dict **dict, struct lns_error **err); /* Write tree TREE that was initially read from TEXT (but might have been * modified) into file OUT using LENS. * * If ERR is non-NULL, *ERR is set to NULL on success, and to an error * message on failure. * * INFO indicates where we are writing to, and its flags indicate whether * to update spans or not. */ void lns_put(struct info *info, FILE *out, struct lens *lens, struct tree *tree, const char *text, int enable_span, struct lns_error **err); /* Free up temporary data structures, most importantly compiled regular expressions */ void lens_release(struct lens *lens); void free_lens(struct lens *lens); /* * Encoding of tree levels into strings */ /* Special characters used when encoding one level of the tree as a string. * We encode one tree node as KEY . ENC_EQ . VALUE . ENC_SLASH; if KEY or * VALUE are NULL, we use ENC_NULL, which is the empty string. This has the * effect that NULL strings are treated the same as empty strings. * * This encoding is used both for actual trees in the put direction, and to * produce regular expressions describing one level in the tree (we * disregard subtrees) * * For this to work, neither ENC_EQ nor ENC_SLASH can be allowed in a * VALUE; we do this behind the scenes by rewriting regular expressions for * values. */ #define ENC_EQ "\003" #define ENC_SLASH "\004" #define ENC_NULL "" #define ENC_EQ_CH (ENC_EQ[0]) #define ENC_SLASH_CH (ENC_SLASH[0]) /* The reserved range of characters that we do not allow in user-supplied regular expressions, since we need them for internal bookkeeping. This range must include the ENC_* characters */ #define RESERVED_FROM "\001" #define RESERVED_TO ENC_SLASH #define RESERVED_FROM_CH (RESERVED_FROM[0]) #define RESERVED_TO_CH ENC_SLASH_CH /* The range of reserved chars as it appears in a regex */ #define RESERVED_RANGE_RX RESERVED_FROM "-" RESERVED_TO /* The equivalent of "." in a regexp for display */ #define RESERVED_DOT_RX "[^" RESERVED_RANGE_RX "\n]" /* The length of the string S encoded */ #define ENCLEN(s) ((s) == NULL ? strlen(ENC_NULL) : strlen(s)) #define ENCSTR(s) ((s) == NULL ? ENC_NULL : s) /* helper to access first and last child */ #define child_first(l) (l)->children[0] #define child_last(l) (l)->children[(l)->nchildren - 1] /* Format an encoded level as * { key1 = value1 } { key2 = value2 } .. { keyN = valueN } */ char *enc_format(const char *e, size_t len); /* Format an encoded level similar to ENC_FORMAT, but put each tree node * on a new line indented by INDENT spaces. If INDENT is negative, produce the * same output as ENC_FORMAT * { key1 = value1 } { key2 = value2 } .. { keyN = valueN } */ char *enc_format_indent(const char *e, size_t len, int indent); #if ENABLE_DEBUG void dump_lens_tree(struct lens *lens); void dump_lens(FILE *out, struct lens *lens); #endif #endif /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/fa_sym.version0000644000175000017500000000140614161102026013430 00000000000000FA_1.0.0 { global: fa_minimization_algorithm; fa_compile; fa_make_basic; fa_is_basic; fa_minimize; fa_concat; fa_union; fa_intersect; fa_complement; fa_minus; fa_iter; fa_contains; fa_equals; fa_free; fa_dot; fa_overlap; fa_example; fa_ambig_example; fa_as_regexp; fa_restrict_alphabet; fa_expand_char_ranges; local: *; }; FA_1.2.0 { fa_nocase; fa_is_nocase; fa_expand_nocase; } FA_1.0.0; FA_1.4.0 { fa_enumerate; } FA_1.2.0; FA_1.5.0 { fa_json; fa_state_initial; fa_state_is_accepting; fa_state_next; fa_state_num_trans; fa_state_trans; fa_is_deterministic; } FA_1.4.0; augeas-1.13.0/src/fa.h0000644000175000017500000003041414161102026011303 00000000000000/* * fa.h: finite automata * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #ifndef FA_H_ #define FA_H_ #include #include #include #include /* The type for a finite automaton. */ struct fa; /* The type of a state of a finite automaton. The fa_state functions return * pointers to this struct. Those pointers are only valid as long as the * only fa_* functions that are called are fa_state_* functions. For * example, the following code will almost certainly result in a crash (or * worse): * * struct state *s = fa_state_initial(fa); * fa_minimize(fa); * // Crashes as S will likely have been freed * s = fa_state_next(s) */ struct state; /* Denote some basic automata, used by fa_is_basic and fa_make_basic */ enum fa_basic { FA_EMPTY, /* Accepts the empty language, i.e. no strings */ FA_EPSILON, /* Accepts only the empty word */ FA_TOTAL /* Accepts all words */ }; /* Choice of minimization algorithm to use; either Hopcroft's O(n log(n)) * algorithm or Brzozowski's reverse-determinize-reverse-determinize * algorithm. While the latter has exponential complexity in theory, it * works quite well for some cases. */ enum fa_minimization_algorithms { FA_MIN_HOPCROFT, FA_MIN_BRZOZOWSKI }; /* Which minimization algorithm to use in FA_MINIMIZE. The library * minimizes internally at certain points, too. * * Defaults to FA_MIN_HOPCROFT */ extern int fa_minimization_algorithm; /* Unless otherwise mentioned, automata passed into routines are never * modified. It is the responsibility of the caller to free automata * returned by any of these routines when they are no longer needed. */ /* * Compile the regular expression RE of length SIZE into an automaton. The * return value is the same as the return value for the POSIX function * regcomp. The syntax for regular expressions is extended POSIX syntax, * with the difference that '.' does not match newlines. * * On success, FA points to the newly allocated automaton constructed for * RE, and the function returns REG_NOERROR. Otherwise, FA is NULL, and the * return value indicates the error. * * The FA is case sensitive. Call FA_NOCASE to switch it to * case-insensitive. */ int fa_compile(const char *re, size_t size, struct fa **fa); /* Make a new automaton that accepts one of the basic languages defined in * the enum FA_BASIC. */ struct fa *fa_make_basic(unsigned int basic); /* Return 1 if FA accepts the basic language BASIC, which must be one of * the constants from enum FA_BASIC. */ int fa_is_basic(struct fa *fa, unsigned int basic); /* Minimize FA using the currently-set fa_minimization_algorithm. * As a side effect, the automaton will also be deterministic after being * minimized. Modifies the automaton in place. */ int fa_minimize(struct fa *fa); /* Return a finite automaton that accepts the concatenation of the * languages for FA1 and FA2, i.e. L(FA1).L(FA2) */ struct fa *fa_concat(struct fa *fa1, struct fa *fa2); /* Return a finite automaton that accepts the union of the languages that * FA1 and FA2 accept (the '|' operator in regular expressions). */ struct fa *fa_union(struct fa *fa1, struct fa *fa2); /* Return a finite automaton that accepts the intersection of the languages * of FA1 and FA2. */ struct fa *fa_intersect(struct fa *fa1, struct fa *fa2); /* Return a finite automaton that accepts the complement of the language of * FA, i.e. the set of all words not accepted by FA */ struct fa *fa_complement(struct fa *fa); /* Return a finite automaton that accepts the set difference of the * languages of FA1 and FA2, i.e. L(FA1)\L(FA2) */ struct fa *fa_minus(struct fa *fa1, struct fa *fa2); /* Return a finite automaton that accepts a repetition of the language that * FA accepts. If MAX == -1, the returned automaton accepts arbitrarily * long repetitions. MIN must be 0 or bigger, and unless MAX == -1, MIN * must be less or equal to MAX. If MIN is greater than 0, the returned * automaton accepts only words that have at least MIN repetitions of words * from L(FA). * * The following common regexp repetitios are achieved by the following * calls (using a lose notation equating automata and their languages): * * - FA* = FA_ITER(FA, 0, -1) * - FA+ = FA_ITER(FA, 1, -1) * - FA? = FA_ITER(FA, 0, 1) * - FA{n,m} = FA_ITER(FA, n, m) with 0 <= n and m = -1 or n <= m */ struct fa *fa_iter(struct fa *fa, int min, int max); /* If successful, returns 1 if the language of FA1 is contained in the language * of FA2, 0 otherwise. Returns a negative number if an error occurred. */ int fa_contains(struct fa *fa1, struct fa *fa2); /* If successful, returns 1 if the language of FA1 equals the language of FA2, * 0 otherwise. Returns a negative number if an error occurred. */ int fa_equals(struct fa *fa1, struct fa *fa2); /* Free all memory used by FA */ void fa_free(struct fa *fa); /* Print FA to OUT as a graphviz dot file */ void fa_dot(FILE *out, struct fa *fa); /* Return a finite automaton that accepts the overlap of the languages of * FA1 and FA2. The overlap of two languages is the set of strings that can * be split in more than one way into a left part accepted by FA1 and a * right part accepted by FA2. */ struct fa *fa_overlap(struct fa *fa1, struct fa *fa2); /* Produce an example for the language of FA. The example is not * necessarily the shortest possible. The implementation works very hard to * have printable characters (preferably alphanumeric) in the example, and * to avoid just an empty word. * * *EXAMPLE will be the example, which may be NULL. If it is non-NULL, * EXAMPLE_LEN will hold the length of the example. * * Return 0 on success, and a negative numer on error. On error, *EXAMPLE * will be NULL. * * If *EXAMPLE is set, it is the caller's responsibility to free the string * by calling free(). */ int fa_example(struct fa *fa, char **example, size_t *example_len); /* Produce an example of an ambiguous word for the concatenation of the * languages of FA1 and FA2. The return value is such a word (which must be * freed by the caller) if it exists. If none exists, NULL is returned. * * The returned word is of the form UPV and PV and V are set to the first * character of P and V in the returned word. The word UPV has the property * that U and UP are accepted by FA1 and that PV and V are accepted by FA2. * * Neither the language of FA1 or of FA2 may contain words with the * characters '\001' and '\002', as they are used during construction of * the ambiguous word. * * UPV_LEN will be set to the length of the entire string UPV * * Returns 0 on success, and a negative number on failure. On failure, UPV, * PV, and V will be NULL */ int fa_ambig_example(struct fa *fa1, struct fa *fa2, char **upv, size_t *upv_len, char **pv, char **v); /* Convert the finite automaton FA into a regular expression and set REGEXP * to point to that. When REGEXP is compiled into another automaton, it is * guaranteed that that automaton and FA accept the same language. * * The code tries to be semi-clever about keeping the generated regular * expression short; to guarantee reasonably short regexps, the automaton * should be minimized before passing it to this routine. * * On success, REGEXP_LEN is set to the length of REGEXP * * Return 0 on success, and a negative number on failure. The only reason * for FA_AS_REGEXP to fail is running out of memory. */ int fa_as_regexp(struct fa *fa, char **regexp, size_t *regexp_len); /* Given the regular expression REGEXP construct a new regular expression * NEWREGEXP that does not match strings containing any of the characters * in the range FROM to TO, with the endpoints included. * * The new regular expression is constructed by removing the range FROM to * TO from all character sets in REGEXP; if any of the characters [FROM, * TO] appear outside a character set in REGEXP, return -2. * * Return 0 if NEWREGEXP was constructed successfully, -1 if an internal * error happened (e.g., an allocation failed) and -2 if NEWREGEXP can not * be constructed because any character in the range [FROM, TO] appears * outside of a character set. * * Return a positive value if REGEXP is not syntactically valid; the value * returned is one of the REG_ERRCODE_T POSIX error codes. Return 0 on * success and -1 if an allocation fails. */ int fa_restrict_alphabet(const char *regexp, size_t regexp_len, char **newregexp, size_t *newregexp_len, char from, char to); /* Convert REGEXP into one that does not use ranges inside character * classes. * * Return a positive value if REGEXP is not syntactically valid; the value * returned is one of the REG_ERRCODE_T POSIX error codes. Return 0 on * success and -1 if an allocation fails. */ int fa_expand_char_ranges(const char *regexp, size_t regexp_len, char **newregexp, size_t *newregexp_len); /* Modify FA so that it matches ignoring case. * * Returns 0 on success, and -1 if an allocation fails. On failure, the * automaton is not guaranteed to represent anything sensible. */ int fa_nocase(struct fa *fa); /* Return 1 if FA matches ignoring case, 0 if matches are case sensitive */ int fa_is_nocase(struct fa *fa); /* Assume REGEXP is a case-insensitive regular expression, and convert it * to one that matches the same strings when used case sensitively. All * occurrences of individual letters c in the regular expression will be * replaced by character sets [cC], and lower/upper case characters are * added to character sets as needed. * * Return a positive value if REGEXP is not syntactically valid; the value * returned is one of the REG_ERRCODE_T POSIX error codes. Return 0 on * success and -1 if an allocation fails. */ int fa_expand_nocase(const char *regexp, size_t regexp_len, char **newregexp, size_t *newregexp_len); /* Generate up to LIMIT words from the language of FA, which is assumed to * be finite. The words are returned in WORDS, which is allocated by this * function and must be freed by the caller. * * If FA accepts the empty word, the empty string will be included in * WORDS. * * Return the number of generated words on success, -1 if we run out of * memory, and -2 if FA has more than LIMIT words. */ int fa_enumerate(struct fa *fa, int limit, char ***words); /* Print FA to OUT as a JSON file. State 0 is always the initial one. * Returns 0 on success, and -1 on failure. */ int fa_json(FILE *out, struct fa *fa); /* Returns true if the FA is deterministic and 0 otherwise */ bool fa_is_deterministic(struct fa *fa); /* Return the initial state */ struct state *fa_state_initial(struct fa *fa); /* Return true if this is an accepting state */ bool fa_state_is_accepting(struct state *st); /* Return the next state; return NULL if there are no more states */ struct state* fa_state_next(struct state *st); /* Return the number of transitions for a state */ size_t fa_state_num_trans(struct state *st); /* Produce details about the i-th transition. * * On success, *to points to the destination state of the transition and * the interval [min-max] is the label of the transition. * * On failure, *to, min and max are not modified. * * Return 0 on success and -1 when I is larger than the number of * transitions of ST. */ int fa_state_trans(struct state *st, size_t i, struct state **to, unsigned char *min, unsigned char *max); #endif /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/memory.c0000644000175000017500000000655714161102026012233 00000000000000/* * memory.c: safer memory allocation * * Copyright (C) 2008-2016 Daniel P. Berrange * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include "memory.h" /* Return 1 if an array of N objects, each of size S, cannot exist due to size arithmetic overflow. S must be positive and N must be nonnegative. This is a macro, not an inline function, so that it works correctly even when SIZE_MAX < N. By gnulib convention, SIZE_MAX represents overflow in size calculations, so the conservative dividend to use here is SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value. However, malloc (SIZE_MAX) fails on all known hosts where sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for exactly-SIZE_MAX allocations on such hosts; this avoids a test and branch when S is known to be 1. */ # define xalloc_oversized(n, s) \ ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n)) /** * mem_alloc_n: * @ptrptr: pointer to pointer for address of allocated memory * @size: number of bytes to allocate * @count: number of elements to allocate * * Allocate an array of memory 'count' elements long, * each with 'size' bytes. Return the address of the * allocated memory in 'ptrptr'. The newly allocated * memory is filled with zeros. * * Returns -1 on failure to allocate, zero on success */ int mem_alloc_n(void *ptrptr, size_t size, size_t count) { if (AUGEAS_UNLIKELY(size == 0 || count == 0)) { *(void **)ptrptr = NULL; return 0; } *(void**)ptrptr = calloc(count, size); if (AUGEAS_UNLIKELY(*(void**)ptrptr == NULL)) return -1; return 0; } /** * virReallocN: * @ptrptr: pointer to pointer for address of allocated memory * @size: number of bytes to allocate * @count: number of elements in array * * Resize the block of memory in 'ptrptr' to be an array of * 'count' elements, each 'size' bytes in length. Update 'ptrptr' * with the address of the newly allocated memory. On failure, * 'ptrptr' is not changed and still points to the original memory * block. The newly allocated memory is filled with zeros. * * Returns -1 on failure to allocate, zero on success */ int mem_realloc_n(void *ptrptr, size_t size, size_t count) { void *tmp; if (AUGEAS_UNLIKELY(size == 0 || count == 0)) { free(*(void **)ptrptr); *(void **)ptrptr = NULL; return 0; } if (AUGEAS_UNLIKELY(xalloc_oversized(count, size))) { errno = ENOMEM; return -1; } tmp = realloc(*(void**)ptrptr, size * count); if (AUGEAS_UNLIKELY(!tmp)) return -1; *(void**)ptrptr = tmp; return 0; } augeas-1.13.0/src/augtool.c0000644000175000017500000005330414161102026012365 00000000000000/* * augtool.c: * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include "augeas.h" #include "internal.h" #include "safe-alloc.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /* Global variables */ static augeas *aug = NULL; static const char *const progname = "augtool"; static unsigned int flags = AUG_NONE; const char *root = NULL; char *loadpath = NULL; char *transforms = NULL; char *loadonly = NULL; size_t transformslen = 0; size_t loadonlylen = 0; const char *inputfile = NULL; int echo_commands = 0; /* Gets also changed in main_loop */ bool print_version = false; bool auto_save = false; bool interactive = false; bool timing = false; /* History file is ~/.augeas/history */ char *history_file = NULL; #define AUGTOOL_PROMPT "augtool> " /* * General utilities */ /* Private copy of xasprintf from internal to avoid Multiple definition in * static builds. */ static int _xasprintf(char **strp, const char *format, ...) { va_list args; int result; va_start (args, format); result = vasprintf (strp, format, args); va_end (args); if (result < 0) *strp = NULL; return result; } static int child_count(const char *path) { char *pat = NULL; int r; if (path[strlen(path)-1] == SEP) r = asprintf(&pat, "%s*", path); else r = asprintf(&pat, "%s/*", path); if (r < 0) return -1; r = aug_match(aug, pat, NULL); free(pat); return r; } static char *readline_path_generator(const char *text, int state) { static int current = 0; static char **children = NULL; static int nchildren = 0; static char *ctx = NULL; char *end = strrchr(text, SEP); if (end != NULL) end += 1; if (state == 0) { char *path; if (end == NULL) { if ((path = strdup("*")) == NULL) return NULL; } else { if (ALLOC_N(path, end - text + 2) < 0) return NULL; strncpy(path, text, end - text); strcat(path, "*"); } for (;current < nchildren; current++) free((void *) children[current]); free((void *) children); nchildren = aug_match(aug, path, &children); current = 0; ctx = NULL; if (path[0] != SEP) aug_get(aug, AUGEAS_CONTEXT, (const char **) &ctx); free(path); } if (end == NULL) end = (char *) text; while (current < nchildren) { char *child = children[current]; current += 1; char *chend = strrchr(child, SEP) + 1; if (STREQLEN(chend, end, strlen(end))) { if (child_count(child) > 0) { char *c = realloc(child, strlen(child)+2); if (c == NULL) { free(child); return NULL; } child = c; strcat(child, "/"); } /* strip off context if the user didn't give it */ if (ctx != NULL) { int ctxidx = strlen(ctx); if (child[ctxidx] == SEP) ctxidx++; char *c = strdup(&child[ctxidx]); free(child); if (c == NULL) return NULL; child = c; } rl_filename_completion_desired = 1; rl_completion_append_character = '\0'; return child; } else { free(child); } } return NULL; } static char *readline_command_generator(const char *text, int state) { // FIXME: expose somewhere under /augeas static const char *const commands[] = { "quit", "clear", "defnode", "defvar", "get", "label", "ins", "load", "ls", "match", "mv", "cp", "rename", "print", "dump-xml", "rm", "save", "set", "setm", "clearm", "span", "store", "retrieve", "transform", "load-file", "help", "touch", "insert", "move", "copy", "errors", "source", "context", "info", "count", "preview", NULL }; static int current = 0; const char *name; if (state == 0) current = 0; rl_completion_append_character = ' '; while ((name = commands[current]) != NULL) { current += 1; if (STREQLEN(text, name, strlen(text))) return strdup(name); } return NULL; } #ifndef HAVE_RL_COMPLETION_MATCHES typedef char *rl_compentry_func_t(const char *, int); static char **rl_completion_matches(ATTRIBUTE_UNUSED const char *text, ATTRIBUTE_UNUSED rl_compentry_func_t *func) { return NULL; } #endif #ifndef HAVE_RL_CRLF static int rl_crlf(void) { if (rl_outstream != NULL) putc('\n', rl_outstream); return 0; } #endif #ifndef HAVE_RL_REPLACE_LINE static void rl_replace_line(ATTRIBUTE_UNUSED const char *text, ATTRIBUTE_UNUSED int clear_undo) { return; } #endif static char **readline_completion(const char *text, int start, ATTRIBUTE_UNUSED int end) { if (start == 0) return rl_completion_matches(text, readline_command_generator); else return rl_completion_matches(text, readline_path_generator); return NULL; } static char *get_home_dir(uid_t uid) { char *strbuf; char *result; struct passwd pwbuf; struct passwd *pw = NULL; long val = sysconf(_SC_GETPW_R_SIZE_MAX); if (val < 0) { // The libc won't tell us how big a buffer to reserve. // Let's hope that 16k is enough (it really should be). val = 16*1024; } size_t strbuflen = (size_t) val; if (ALLOC_N(strbuf, strbuflen) < 0) return NULL; if (getpwuid_r(uid, &pwbuf, strbuf, strbuflen, &pw) != 0 || pw == NULL) { free(strbuf); // Try to get the user's home dir from the environment char *env = getenv("HOME"); if (env != NULL) { return strdup(env); } return NULL; } result = strdup(pw->pw_dir); free(strbuf); return result; } /* Inspired from: * https://thoughtbot.com/blog/tab-completion-in-gnu-readline */ static int quote_detector(char *str, int index) { return index > 0 && str[index - 1] == '\\' && quote_detector(str, index - 1) == 0; } static void readline_init(void) { rl_readline_name = "augtool"; rl_attempted_completion_function = readline_completion; rl_completion_entry_function = readline_path_generator; rl_completer_quote_characters = "\"'"; rl_completer_word_break_characters = (char *) " "; rl_char_is_quoted_p = "e_detector; /* Set up persistent history */ char *home_dir = get_home_dir(getuid()); char *history_dir = NULL; if (home_dir == NULL) goto done; if (_xasprintf(&history_dir, "%s/.augeas", home_dir) < 0) goto done; if (mkdir(history_dir, 0755) < 0 && errno != EEXIST) goto done; if (_xasprintf(&history_file, "%s/history", history_dir) < 0) goto done; stifle_history(500); read_history(history_file); done: free(home_dir); free(history_dir); } __attribute__((noreturn)) static void help(void) { fprintf(stderr, "Usage: %s [OPTIONS] [COMMAND]\n", progname); fprintf(stderr, "Load the Augeas tree and modify it. If no COMMAND is given, run interactively\n"); fprintf(stderr, "Run '%s help' to get a list of possible commands.\n", progname); fprintf(stderr, "\nOptions:\n\n"); fprintf(stderr, " -c, --typecheck typecheck lenses\n"); fprintf(stderr, " -b, --backup preserve originals of modified files with\n" " extension '.augsave'\n"); fprintf(stderr, " -n, --new save changes in files with extension '.augnew',\n" " leave original unchanged\n"); fprintf(stderr, " -r, --root ROOT use ROOT as the root of the filesystem\n"); fprintf(stderr, " -I, --include DIR search DIR for modules; can be given multiple times\n"); fprintf(stderr, " -t, --transform XFM add a file transform; uses the 'transform' command\n" " syntax, e.g. -t 'Fstab incl /etc/fstab.bak'\n"); fprintf(stderr, " -l, --load-file FILE load individual FILE in the tree\n"); fprintf(stderr, " -e, --echo echo commands when reading from a file\n"); fprintf(stderr, " -f, --file FILE read commands from FILE\n"); fprintf(stderr, " -s, --autosave automatically save at the end of instructions\n"); fprintf(stderr, " -i, --interactive run an interactive shell after evaluating\n" " the commands in STDIN and FILE\n"); fprintf(stderr, " -S, --nostdinc do not search the builtin default directories\n" " for modules\n"); fprintf(stderr, " -L, --noload do not load any files into the tree on startup\n"); fprintf(stderr, " -A, --noautoload do not autoload modules from the search path\n"); fprintf(stderr, " --span load span positions for nodes related to a file\n"); fprintf(stderr, " --timing after executing each command, show how long it took\n"); fprintf(stderr, " --version print version information and exit.\n"); exit(EXIT_FAILURE); } static void parse_opts(int argc, char **argv) { int opt; size_t loadpathlen = 0; enum { VAL_VERSION = CHAR_MAX + 1, VAL_SPAN = VAL_VERSION + 1, VAL_TIMING = VAL_SPAN + 1 }; struct option options[] = { { "help", 0, 0, 'h' }, { "typecheck", 0, 0, 'c' }, { "backup", 0, 0, 'b' }, { "new", 0, 0, 'n' }, { "root", 1, 0, 'r' }, { "include", 1, 0, 'I' }, { "transform", 1, 0, 't' }, { "load-file", 1, 0, 'l' }, { "echo", 0, 0, 'e' }, { "file", 1, 0, 'f' }, { "autosave", 0, 0, 's' }, { "interactive", 0, 0, 'i' }, { "nostdinc", 0, 0, 'S' }, { "noload", 0, 0, 'L' }, { "noautoload", 0, 0, 'A' }, { "span", 0, 0, VAL_SPAN }, { "timing", 0, 0, VAL_TIMING }, { "version", 0, 0, VAL_VERSION }, { 0, 0, 0, 0} }; int idx; while ((opt = getopt_long(argc, argv, "hnbcr:I:t:l:ef:siSLA", options, &idx)) != -1) { switch(opt) { case 'c': flags |= AUG_TYPE_CHECK; break; case 'b': flags |= AUG_SAVE_BACKUP; break; case 'n': flags |= AUG_SAVE_NEWFILE; break; case 'h': help(); break; case 'r': root = optarg; break; case 'I': argz_add(&loadpath, &loadpathlen, optarg); break; case 't': argz_add(&transforms, &transformslen, optarg); break; case 'l': // --load-file implies --noload flags |= AUG_NO_LOAD; argz_add(&loadonly, &loadonlylen, optarg); break; case 'e': echo_commands = 1; break; case 'f': inputfile = optarg; break; case 's': auto_save = true; break; case 'i': interactive = true; break; case 'S': flags |= AUG_NO_STDINC; break; case 'L': flags |= AUG_NO_LOAD; break; case 'A': flags |= AUG_NO_MODL_AUTOLOAD; break; case VAL_VERSION: flags |= AUG_NO_MODL_AUTOLOAD; print_version = true; break; case VAL_SPAN: flags |= AUG_ENABLE_SPAN; break; case VAL_TIMING: timing = true; break; default: fprintf(stderr, "Try '%s --help' for more information.\n", progname); exit(EXIT_FAILURE); break; } } argz_stringify(loadpath, loadpathlen, PATH_SEP_CHAR); } static void print_version_info(void) { const char *version; int r; r = aug_get(aug, "/augeas/version", &version); if (r != 1) goto error; fprintf(stderr, "augtool %s \n", version); fprintf(stderr, "Copyright (C) 2007-2016 David Lutterkort\n"); fprintf(stderr, "License LGPLv2+: GNU LGPL version 2.1 or later\n"); fprintf(stderr, " \n"); fprintf(stderr, "This is free software: you are free to change and redistribute it.\n"); fprintf(stderr, "There is NO WARRANTY, to the extent permitted by law.\n\n"); fprintf(stderr, "Written by David Lutterkort\n"); return; error: fprintf(stderr, "Something went terribly wrong internally - please file a bug\n"); } static void print_time_taken(const struct timeval *start, const struct timeval *stop) { time_t elapsed = (stop->tv_sec - start->tv_sec)*1000 + (stop->tv_usec - start->tv_usec)/1000; printf("Time: %ld ms\n", elapsed); } static int run_command(const char *line, bool with_timing) { int result; struct timeval stop, start; gettimeofday(&start, NULL); result = aug_srun(aug, stdout, line); gettimeofday(&stop, NULL); if (with_timing && result >= 0) { print_time_taken(&start, &stop); } if (isatty(fileno(stdin))) add_history(line); return result; } static void print_aug_error(void) { if (aug_error(aug) == AUG_ENOMEM) { fprintf(stderr, "Out of memory.\n"); return; } if (aug_error(aug) != AUG_NOERROR) { fprintf(stderr, "error: %s\n", aug_error_message(aug)); if (aug_error_minor_message(aug) != NULL) fprintf(stderr, "error: %s\n", aug_error_minor_message(aug)); if (aug_error_details(aug) != NULL) { fputs(aug_error_details(aug), stderr); fprintf(stderr, "\n"); } } } static void sigint_handler(ATTRIBUTE_UNUSED int signum) { // Cancel the current line of input, along with undo info for that line. rl_replace_line("", 1); // Move the cursor to the next screen line, then force a re-display. rl_crlf(); rl_forced_update_display(); } static void install_signal_handlers(void) { // On Ctrl-C, cancel the current line (rather than exit the program). struct sigaction sigint_action; MEMZERO(&sigint_action, 1); sigint_action.sa_handler = sigint_handler; sigemptyset(&sigint_action.sa_mask); sigint_action.sa_flags = 0; sigaction(SIGINT, &sigint_action, NULL); } static int main_loop(void) { char *line = NULL; int ret = 0; char inputline [128]; int code; bool end_reached = false; bool get_line = true; bool in_interactive = false; if (inputfile) { if (freopen(inputfile, "r", stdin) == NULL) { char *msg = NULL; if (asprintf(&msg, "Failed to open %s", inputfile) < 0) perror("Failed to open input file"); else perror(msg); return -1; } } install_signal_handlers(); // make readline silent by default echo_commands = echo_commands || isatty(fileno(stdin)); if (echo_commands) rl_outstream = NULL; else rl_outstream = fopen("/dev/null", "w"); while(1) { if (get_line) { line = readline(AUGTOOL_PROMPT); } else { line = NULL; } if (line == NULL) { if (!isatty(fileno(stdin)) && interactive && !in_interactive) { in_interactive = true; if (echo_commands) printf("\n"); echo_commands = true; // reopen in stream if (freopen("/dev/tty", "r", stdin) == NULL) { perror("Failed to open terminal for reading"); return -1; } rl_instream = stdin; // reopen stdout and stream to a tty if originally silenced or // not connected to a tty, for full interactive mode if (rl_outstream == NULL || !isatty(fileno(rl_outstream))) { if (rl_outstream != NULL) { fclose(rl_outstream); rl_outstream = NULL; } if (freopen("/dev/tty", "w", stdout) == NULL) { perror("Failed to reopen stdout"); return -1; } rl_outstream = stdout; } continue; } if (auto_save) { strncpy(inputline, "save", sizeof(inputline)); line = inputline; if (echo_commands) printf("%s\n", line); auto_save = false; } else { end_reached = true; } get_line = false; } if (end_reached) { if (echo_commands) printf("\n"); return ret; } if (*line == '\0' || *line == '#') { free(line); continue; } code = run_command(line, timing); if (code == -2) { free(line); return ret; } if (code < 0) { ret = -1; print_aug_error(); } if (line != inputline) free(line); } } static int run_args(int argc, char **argv) { size_t len = 0; char *line = NULL; int code; for (int i=0; i < argc; i++) len += strlen(argv[i]) + 1; if (ALLOC_N(line, len + 1) < 0) return -1; for (int i=0; i < argc; i++) { strcat(line, argv[i]); strcat(line, " "); } if (echo_commands) printf("%s%s\n", AUGTOOL_PROMPT, line); code = run_command(line, timing); free(line); if (code >= 0 && auto_save) if (echo_commands) printf("%ssave\n", AUGTOOL_PROMPT); code = run_command("save", false); if (code < 0) { code = -1; print_aug_error(); } return (code >= 0 || code == -2) ? 0 : -1; } static void add_transforms(char *ts, size_t tslen) { char *command; int r; char *t = NULL; bool added_transform = false; while ((t = argz_next(ts, tslen, t))) { r = _xasprintf(&command, "transform %s", t); if (r < 0) fprintf(stderr, "error: Failed to add transform %s: could not allocate memory\n", t); r = aug_srun(aug, stdout, command); if (r < 0) fprintf(stderr, "error: Failed to add transform %s: %s\n", t, aug_error_message(aug)); free(command); added_transform = true; } if (added_transform) { r = aug_load(aug); if (r < 0) fprintf(stderr, "error: Failed to load with new transforms: %s\n", aug_error_message(aug)); } } static void load_files(char *ts, size_t tslen) { char *command; int r; char *t = NULL; while ((t = argz_next(ts, tslen, t))) { r = _xasprintf(&command, "load-file %s", t); if (r < 0) fprintf(stderr, "error: Failed to load file %s: could not allocate memory\n", t); r = aug_srun(aug, stdout, command); if (r < 0) fprintf(stderr, "error: Failed to load file %s: %s\n", t, aug_error_message(aug)); free(command); } } int main(int argc, char **argv) { int r; struct timeval start, stop; setlocale(LC_ALL, ""); parse_opts(argc, argv); if (timing) { printf("Initializing augeas ... "); fflush(stdout); } gettimeofday(&start, NULL); aug = aug_init(root, loadpath, flags|AUG_NO_ERR_CLOSE); gettimeofday(&stop, NULL); if (timing) { printf("done\n"); print_time_taken(&start, &stop); } if (aug == NULL || aug_error(aug) != AUG_NOERROR) { fprintf(stderr, "Failed to initialize Augeas\n"); if (aug != NULL) print_aug_error(); exit(EXIT_FAILURE); } load_files(loadonly, loadonlylen); add_transforms(transforms, transformslen); if (print_version) { print_version_info(); return EXIT_SUCCESS; } readline_init(); if (optind < argc) { // Accept one command from the command line r = run_args(argc - optind, argv+optind); } else { r = main_loop(); } if (history_file != NULL) write_history(history_file); aug_close(aug); return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/info.h0000644000175000017500000000464014161102026011652 00000000000000/* * info.h: filename/linenumber information for parser/interpreter * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #ifndef INFO_H_ #define INFO_H_ #include #include #include #include "ref.h" /* Reference-counted strings */ struct string { ref_t ref; char *str; }; struct string *make_string(char *str); /* Duplicate a string; if STR is NULL, use the empty string "" */ struct string *dup_string(const char *str); /* Do not call directly, use UNREF instead */ void free_string(struct string *string); /* File information */ struct info { /* There is only one struct error for each Augeas instance */ struct error *error; struct string *filename; uint16_t first_line; uint16_t first_column; uint16_t last_line; uint16_t last_column; ref_t ref; }; struct span { struct string *filename; uint label_start; uint label_end; uint value_start; uint value_end; uint span_start; uint span_end; }; char *format_info(struct info *info); void print_info(FILE *out, struct info *info); /* Return true if typechecking is turned on. (This uses the somewhat gross * fact that we can get at the augeas flags through error->aug) */ bool typecheck_p(const struct info *info); /* Do not call directly, use UNREF instead */ void free_info(struct info *info); struct span *make_span(struct info *info); void free_span(struct span *node_info); void update_span(struct span *node_info, int x, int y); void print_span(struct span *node_info); #endif /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/regexp.c0000644000175000017500000003660214161102026012207 00000000000000/* * regexp.c: * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include #include "internal.h" #include "syntax.h" #include "memory.h" #include "errcode.h" static const struct string empty_pattern_string = { .ref = REF_MAX, .str = (char *) "()" }; static const struct string *const empty_pattern = &empty_pattern_string; char *regexp_escape(const struct regexp *r) { char *pat = NULL; if (r == NULL) return strdup(""); #if !HAVE_USELOCALE char *nre = NULL; int ret; size_t nre_len; /* Use a range with from > to to force conversion of ranges into * short form */ ret = fa_restrict_alphabet(r->pattern->str, strlen(r->pattern->str), &nre, &nre_len, 2, 1); if (ret == 0) { pat = escape(nre, nre_len, RX_ESCAPES); free(nre); } #endif if (pat == NULL) { /* simplify the regexp by removing some artifacts of reserving chanaracters for internal purposes */ if (index(r->pattern->str, RESERVED_FROM_CH)) { char *s = strdup(r->pattern->str); char *t = s; for (char *p = s; *p; p++) { if (STREQLEN(p, RESERVED_RANGE_RX, strlen(RESERVED_RANGE_RX))) { /* Completely eliminate mentions of the reserved range */ p += strlen(RESERVED_RANGE_RX); } else if (STREQLEN(p, RESERVED_DOT_RX, strlen(RESERVED_DOT_RX))) { /* Replace what amounts to a '.' by one */ p += strlen(RESERVED_DOT_RX); *t++ = '.'; } *t++ = *p; } *t = '\0'; pat = escape(s, -1, RX_ESCAPES); free(s); } else { pat = escape(r->pattern->str, -1, RX_ESCAPES); } } if (pat == NULL) return NULL; /* Remove unneeded '()' from pat */ for (int changed = 1; changed;) { changed = 0; for (char *p = pat; *p != '\0'; p++) { if (*p == '(' && p[1] == ')') { memmove(p, p+2, strlen(p+2)+1); changed = 1; } } } if (pat[0] == '(' && pat[strlen(pat)-1] == ')') { int level = 1; for (int i=1; i < strlen(pat)-1; i++) { if (pat[i] == '(') level += 1; if (pat[i] == ')') level -= 1; if (level == 0) break; } if (level == 1) { memmove(pat, pat+1, strlen(pat+1)+1); pat[strlen(pat)-1] = '\0'; } } return pat; } void print_regexp(FILE *out, struct regexp *r) { if (r == NULL) { fprintf(out, ""); return; } fputc('/', out); if (r->pattern == NULL) fprintf(out, "%p", r); else { char *rx; size_t rx_len; fa_restrict_alphabet(r->pattern->str, strlen(r->pattern->str), &rx, &rx_len, 2, 1); print_chars(out, rx, rx_len); FREE(rx); } fputc('/', out); if (r->nocase) fputc('i', out); } struct regexp * make_regexp_unescape(struct info *info, const char *pat, int nocase) { char *p = unescape(pat, strlen(pat), NULL); if (p == NULL) return NULL; return make_regexp(info, p, nocase); } struct regexp *make_regexp(struct info *info, char *pat, int nocase) { struct regexp *regexp; make_ref(regexp); regexp->info = ref(info); make_ref(regexp->pattern); regexp->pattern->str = pat; regexp->nocase = nocase; return regexp; } /* Take a POSIX glob and turn it into a regexp. The regexp is constructed * by doing the following translations of characters in the string: * * -> [^/]* * ? -> [^/] * leave characters escaped with a backslash alone * escape any of ".|{}()+^$" with a backslash * * Note that that ignores some of the finer points of globs, like * complementation. */ struct regexp *make_regexp_from_glob(struct info *info, const char *glob) { static const char *const star = "[^/]*"; static const char *const qmark = "[^/]"; static const char *const special = ".|{}()+^$"; int newlen = strlen(glob); char *pat = NULL; for (const char *s = glob; *s; s++) { if (*s == '\\' && *(s+1)) s += 1; else if (*s == '*') newlen += strlen(star)-1; else if (*s == '?') newlen += strlen(qmark)-1; else if (strchr(special, *s) != NULL) newlen += 1; } if (ALLOC_N(pat, newlen + 1) < 0) return NULL; char *t = pat; for (const char *s = glob; *s; s++) { if (*s == '\\' && *(s+1)) { *t++ = *s++; *t++ = *s; } else if (*s == '*') { t = stpcpy(t, star); } else if (*s == '?') { t = stpcpy(t, qmark); } else if (strchr(special, *s) != NULL) { *t++ = '\\'; *t++ = *s; } else { *t++ = *s; } } return make_regexp(info, pat, 0); } void free_regexp(struct regexp *regexp) { if (regexp == NULL) return; assert(regexp->ref == 0); unref(regexp->info, info); unref(regexp->pattern, string); if (regexp->re != NULL) { regfree(regexp->re); free(regexp->re); } free(regexp); } int regexp_is_empty_pattern(struct regexp *r) { for (char *s = r->pattern->str; *s; s++) { if (*s != '(' && *s != ')') return 0; } return 1; } struct regexp *make_regexp_literal(struct info *info, const char *text) { char *pattern, *p; /* Escape special characters in text since it should be taken literally */ if (ALLOC_N(pattern, 2*strlen(text)+1) < 0) return NULL; p = pattern; for (const char *t = text; *t != '\0'; t++) { if ((*t == '\\') && t[1]) { *p++ = *t++; *p++ = *t; } else if (strchr(".|{}[]()+*?", *t) != NULL) { *p++ = '\\'; *p++ = *t; } else { *p++ = *t; } } return make_regexp(info, pattern, 0); } struct regexp * regexp_union(struct info *info, struct regexp *r1, struct regexp *r2) { struct regexp *r[2]; r[0] = r1; r[1] = r2; return regexp_union_n(info, 2, r); } char *regexp_expand_nocase(struct regexp *r) { const char *p = r->pattern->str, *t; char *s = NULL; size_t len; int ret; int psub = 0, rsub = 0; if (! r->nocase) return strdup(p); ret = fa_expand_nocase(p, strlen(p), &s, &len); ERR_NOMEM(ret == REG_ESPACE, r->info); BUG_ON(ret != REG_NOERROR, r->info, NULL); /* Make sure that r->pattern->str and ret have the same number * of parentheses/groups, since our parser critically depends * on the fact that the regexp for a union/concat and those * of its children have groups that are in direct relation */ for (t = p; *t; t++) if (*t == '(') psub += 1; for (t = s; *t; t++) if (*t == '(') rsub += 1; BUG_ON(psub < rsub, r->info, NULL); psub -= rsub; if (psub > 0) { char *adjusted = NULL, *a; if (ALLOC_N(adjusted, strlen(s) + 2*psub + 1) < 0) ERR_NOMEM(true, r->info); a = adjusted; for (int i=0; i < psub; i++) *a++ = '('; a = stpcpy(a, s); for (int i=0; i < psub; i++) *a++ = ')'; free(s); s = adjusted; } error: return s; } static char *append_expanded(struct regexp *r, char **pat, char *p, size_t *len) { char *expanded = NULL; size_t ofs = p - *pat; int ret; expanded = regexp_expand_nocase(r); ERR_BAIL(r->info); *len += strlen(expanded) - strlen(r->pattern->str); ret = REALLOC_N(*pat, *len); ERR_NOMEM(ret < 0, r->info); p = stpcpy(*pat + ofs, expanded); error: FREE(expanded); return p; } struct regexp * regexp_union_n(struct info *info, int n, struct regexp **r) { size_t len = 0; char *pat = NULL, *p, *expanded = NULL; int nnocase = 0, npresent = 0; for (int i=0; i < n; i++) if (r[i] != NULL) { len += strlen(r[i]->pattern->str) + strlen("()|"); npresent += 1; if (r[i]->nocase) nnocase += 1; } bool mixedcase = nnocase > 0 && nnocase < npresent; if (len == 0) return NULL; if (ALLOC_N(pat, len) < 0) return NULL; p = pat; int added = 0; for (int i=0; i < n; i++) { if (r[i] == NULL) continue; if (added > 0) *p++ = '|'; *p++ = '('; if (mixedcase && r[i]->nocase) { p = append_expanded(r[i], &pat, p, &len); ERR_BAIL(r[i]->info); } else { p = stpcpy(p, r[i]->pattern->str); } *p++ = ')'; added += 1; } *p = '\0'; return make_regexp(info, pat, nnocase == npresent); error: FREE(expanded); FREE(pat); return NULL; } struct regexp * regexp_concat(struct info *info, struct regexp *r1, struct regexp *r2) { struct regexp *r[2]; r[0] = r1; r[1] = r2; return regexp_concat_n(info, 2, r); } struct regexp * regexp_concat_n(struct info *info, int n, struct regexp **r) { size_t len = 0; char *pat = NULL, *p, *expanded = NULL; int nnocase = 0, npresent = 0; for (int i=0; i < n; i++) if (r[i] != NULL) { len += strlen(r[i]->pattern->str) + strlen("()"); npresent += 1; if (r[i]->nocase) nnocase += 1; } bool mixedcase = nnocase > 0 && nnocase < npresent; if (len == 0) return NULL; len += 1; if (ALLOC_N(pat, len) < 0) return NULL; p = pat; for (int i=0; i < n; i++) { if (r[i] == NULL) continue; *p++ = '('; if (mixedcase && r[i]->nocase) { p = append_expanded(r[i], &pat, p, &len); ERR_BAIL(r[i]->info); } else { p = stpcpy(p, r[i]->pattern->str); } *p++ = ')'; } *p = '\0'; return make_regexp(info, pat, nnocase == npresent); error: FREE(expanded); FREE(pat); return NULL; } static struct fa *regexp_to_fa(struct regexp *r) { const char *p = r->pattern->str; int ret; struct fa *fa = NULL; ret = fa_compile(p, strlen(p), &fa); ERR_NOMEM(ret == REG_ESPACE, r->info); BUG_ON(ret != REG_NOERROR, r->info, NULL); if (r->nocase) { ret = fa_nocase(fa); ERR_NOMEM(ret < 0, r->info); } return fa; error: fa_free(fa); return NULL; } struct regexp * regexp_minus(struct info *info, struct regexp *r1, struct regexp *r2) { struct regexp *result = NULL; struct fa *fa = NULL, *fa1 = NULL, *fa2 = NULL; int r; char *s = NULL; size_t s_len; fa1 = regexp_to_fa(r1); ERR_BAIL(r1->info); fa2 = regexp_to_fa(r2); ERR_BAIL(r2->info); fa = fa_minus(fa1, fa2); if (fa == NULL) goto error; r = fa_as_regexp(fa, &s, &s_len); if (r < 0) goto error; if (s == NULL) { /* FA is the empty set, which we can't represent as a regexp */ goto error; } if (regexp_c_locale(&s, NULL) < 0) goto error; result = make_regexp(info, s, fa_is_nocase(fa)); s = NULL; done: fa_free(fa); fa_free(fa1); fa_free(fa2); free(s); return result; error: unref(result, regexp); goto done; } struct regexp * regexp_iter(struct info *info, struct regexp *r, int min, int max) { const char *p; char *s; int ret = 0; if (r == NULL) return NULL; p = r->pattern->str; if ((min == 0 || min == 1) && max == -1) { char q = (min == 0) ? '*' : '+'; ret = asprintf(&s, "(%s)%c", p, q); } else if (min == max) { ret = asprintf(&s, "(%s){%d}", p, min); } else { ret = asprintf(&s, "(%s){%d,%d}", p, min, max); } return (ret == -1) ? NULL : make_regexp(info, s, r->nocase); } struct regexp * regexp_maybe(struct info *info, struct regexp *r) { const char *p; char *s; int ret; if (r == NULL) return NULL; p = r->pattern->str; ret = asprintf(&s, "(%s)?", p); return (ret == -1) ? NULL : make_regexp(info, s, r->nocase); } struct regexp *regexp_make_empty(struct info *info) { struct regexp *regexp; make_ref(regexp); if (regexp != NULL) { regexp->info = ref(info); /* Casting away the CONST for EMPTY_PATTERN is ok since it is protected against changes because REF == REF_MAX */ regexp->pattern = (struct string *) empty_pattern; regexp->nocase = 0; } return regexp; } static int regexp_compile_internal(struct regexp *r, const char **c) { /* See the GNU regex manual or regex.h in gnulib for * an explanation of these flags. They are set so that the regex * matcher interprets regular expressions the same way that libfa * does */ static const reg_syntax_t syntax = RE_CONTEXT_INDEP_OPS|RE_CONTEXT_INVALID_OPS|RE_DOT_NOT_NULL |RE_INTERVALS|RE_NO_BK_BRACES|RE_NO_BK_PARENS|RE_NO_BK_REFS |RE_NO_BK_VBAR|RE_NO_EMPTY_RANGES |RE_NO_POSIX_BACKTRACKING|RE_CONTEXT_INVALID_DUP|RE_NO_GNU_OPS; reg_syntax_t old_syntax = re_syntax_options; *c = NULL; if (r->re == NULL) { if (ALLOC(r->re) < 0) return -1; } re_syntax_options = syntax; if (r->nocase) re_syntax_options |= RE_ICASE; *c = re_compile_pattern(r->pattern->str, strlen(r->pattern->str), r->re); re_syntax_options = old_syntax; r->re->regs_allocated = REGS_REALLOCATE; if (*c != NULL) return -1; return 0; } int regexp_compile(struct regexp *r) { const char *c; return regexp_compile_internal(r, &c); } int regexp_check(struct regexp *r, const char **msg) { return regexp_compile_internal(r, msg); } int regexp_match(struct regexp *r, const char *string, const int size, const int start, struct re_registers *regs) { if (r->re == NULL) { if (regexp_compile(r) == -1) return -3; } return re_match(r->re, string, size, start, regs); } int regexp_matches_empty(struct regexp *r) { return regexp_match(r, "", 0, 0, NULL) == 0; } int regexp_nsub(struct regexp *r) { if (r->re == NULL) if (regexp_compile(r) == -1) return -1; return r->re->re_nsub; } void regexp_release(struct regexp *regexp) { if (regexp != NULL && regexp->re != NULL) { regfree(regexp->re); FREE(regexp->re); } } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/syntax.h0000644000175000017500000002055014161102026012243 00000000000000/* * syntax.h: Data types to represent language syntax * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #ifndef SYNTAX_H_ #define SYNTAX_H_ #include #include "internal.h" #include "lens.h" #include "ref.h" #include "fa.h" #include "regexp.h" #include "info.h" void syntax_error(struct info *info, const char *format, ...) ATTRIBUTE_FORMAT(printf, 2, 3); void fatal_error(struct info *info, const char *format, ...) ATTRIBUTE_FORMAT(printf, 2, 3); enum term_tag { A_MODULE, A_BIND, /* Module scope binding of a name */ A_LET, /* local LET .. IN binding */ A_COMPOSE, A_UNION, A_MINUS, A_CONCAT, A_APP, A_VALUE, A_IDENT, A_BRACKET, A_FUNC, A_REP, A_TEST }; enum test_result_tag { TR_CHECK, TR_PRINT, TR_EXN }; enum quant_tag { Q_STAR, Q_PLUS, Q_MAYBE }; struct term { struct term *next; unsigned int ref; struct info *info; struct type *type; /* Filled in by the typechecker */ enum term_tag tag; union { struct { /* A_MODULE */ char *mname; char *autoload; struct term *decls; }; struct { /* A_BIND */ char *bname; struct term *exp; }; struct { /* A_COMPOSE, A_UNION, A_CONCAT, A_APP, A_LET */ struct term *left; struct term *right; }; struct value *value; /* A_VALUE */ struct term *brexp; /* A_BRACKET */ struct string *ident; /* A_IDENT */ struct { /* A_REP */ enum quant_tag quant; struct term *rexp; }; struct { /* A_FUNC */ struct param *param; struct term *body; }; struct { /* A_TEST */ enum test_result_tag tr_tag; struct term *test; struct term *result; }; }; }; struct param { struct info *info; unsigned int ref; struct string *name; struct type *type; }; /* The protoype for the implementation of a native/builtin function in the * interpreter. * * The arguments are passed as a NULL-terminated array of values. */ typedef struct value *(*func_impl)(struct info *, struct value *argv[]); struct native { unsigned int argc; struct type *type; func_impl impl; }; /* An exception in the interpreter. Some exceptions are reported directly * into the central struct error; an exception for those is only generated * to follow the control flow for exceptions. Such exceptions have both * seen and error set to 1. They are the only exceptions with error == 1. * When error == 1, none of the other fields in the exn will be usable. */ struct exn { struct info *info; unsigned int seen : 1; /* Whether the user has seen this EXN */ unsigned int error : 1; char *message; size_t nlines; char **lines; }; /* * Values in the interpreter */ enum value_tag { V_STRING, V_REGEXP, V_LENS, V_TREE, V_FILTER, V_TRANSFORM, V_NATIVE, V_EXN, V_CLOS, V_UNIT }; #define EXN(v) ((v)->tag == V_EXN) struct value { unsigned int ref; struct info *info; enum value_tag tag; /* Nothing in this union for V_UNIT */ union { struct string *string; /* V_STRING */ struct regexp *regexp; /* V_REGEXP */ struct lens *lens; /* V_LENS */ struct native *native; /* V_NATIVE */ struct tree *origin; /* V_TREE */ struct filter *filter; /* V_FILTER */ struct transform *transform; /* V_TRANSFORM */ struct exn *exn; /* V_EXN */ struct { /* V_CLOS */ struct term *func; struct binding *bindings; }; }; }; /* All types except for T_ARROW (functions) are simple. Subtype relations * for the simple types: * T_STRING <: T_REGEXP * and the usual subtype relation for functions. */ enum type_tag { T_STRING, T_REGEXP, T_LENS, T_TREE, T_FILTER, T_TRANSFORM, T_ARROW, T_UNIT }; struct type { unsigned int ref; enum type_tag tag; struct type *dom; /* T_ARROW */ struct type *img; /* T_ARROW */ }; struct binding { unsigned int ref; struct binding *next; struct string *ident; struct type *type; struct value *value; }; /* A module maps names to TYPE * VALUE. */ struct module { unsigned int ref; struct module *next; /* Only used for the global list of modules */ struct transform *autoload; char *name; struct binding *bindings; }; struct type *make_arrow_type(struct type *dom, struct type *img); struct type *make_base_type(enum type_tag tag); /* Do not call this directly. Use unref(t, type) instead */ void free_type(struct type *type); /* Constructors for some terms in syntax.c Constructor assumes ownership of * arguments without incrementing. Caller owns returned objects. */ struct term *make_term(enum term_tag tag, struct info *info); void free_term(struct term *term); struct term *make_param(char *name, struct type *type, struct info *info); struct value *make_value(enum value_tag tag, struct info *info); struct value *make_unit(struct info *info); struct term *make_app_term(struct term *func, struct term *arg, struct info *info); struct term *make_app_ident(char *id, struct term *func, struct info *info); /* Print a tree in the braces style used in modules */ void print_tree_braces(FILE *out, int indent, struct tree *tree); /* Make an EXN value * Receive ownership of INFO * * FORMAT and following arguments are printed to a new string. Caller must * clean those up. */ struct value *make_exn_value(struct info *info, const char *format, ...) ATTRIBUTE_FORMAT(printf, 2, 3); /* Add NLINES lines (passed as const char *) to EXN, which must be a * value with tag V_EXN, created by MAKE_EXN_VALUE. * * The strings belong to EXN * after the call. */ void exn_add_lines(struct value *exn, int nlines, ...); void exn_printf_line(struct value *exn, const char *format, ...) ATTRIBUTE_FORMAT(printf, 2, 3); /* Do not call these directly, use UNREF instead */ void free_value(struct value *v); void free_module(struct module *module); /* Turn a list of PARAMS (represented as terms tagged as A_FUNC with the * param in PARAM) into nested A_FUNC terms */ struct term *build_func(struct term *params, struct term *exp); struct module *module_create(const char *name); #define define_native(error, module, name, argc, impl, types ...) \ define_native_intl(__FILE__, __LINE__, error, module, name, \ argc, impl, ## types) ATTRIBUTE_RETURN_CHECK int define_native_intl(const char *fname, int line, struct error *error, struct module *module, const char *name, int argc, func_impl impl, ...); struct module *builtin_init(struct error *); int load_module_file(struct augeas *aug, const char *filename, const char *name); /* The name of the builtin function that checks recursive lenses */ #define LNS_CHECK_REC_NAME "lns_check_rec" int interpreter_init(struct augeas *aug); struct lens *lens_lookup(struct augeas *aug, const char *qname); #endif /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/info.c0000644000175000017500000001126214161102026011643 00000000000000/* * info.c: filename/linenumber information for parser/interpreter * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include #include "info.h" #include "internal.h" #include "memory.h" #include "ref.h" #include "errcode.h" /* * struct string */ struct string *make_string(char *str) { struct string *string; make_ref(string); string->str = str; return string; } struct string *dup_string(const char *str) { struct string *string; make_ref(string); if (str == NULL) string->str = strdup(""); else string->str = strdup(str); if (string->str == NULL) unref(string, string); return string; } void free_string(struct string *string) { if (string == NULL) return; assert(string->ref == 0); free(string->str); free(string); } /* * struct info */ char *format_info(struct info *info) { const char *fname; char *result = NULL; int r = 0; if (info == NULL) { return strdup("(no file info)"); } int fl = info->first_line, ll = info->last_line; int fc = info->first_column, lc = info->last_column; fname = (info->filename != NULL) ? info->filename->str : "(unknown file)"; if (fl > 0) { if (fl == ll) { if (fc == lc) { r = xasprintf(&result, "%s:%d.%d:", fname, fl, fc); } else { r = xasprintf(&result, "%s:%d.%d-.%d:", fname, fl, fc, lc); } } else { r = xasprintf(&result, "%s:%d.%d-%d.%d:", fname, fl, fc, ll, lc); } } else { r = xasprintf(&result, "%s:", fname); } return (r == -1) ? NULL : result; } void print_info(FILE *out, struct info *info) { if (info == NULL) { fprintf(out, "(no file info):"); return; } fprintf(out, "%s:", info->filename != NULL ? info->filename->str : "(unknown file)"); if (info->first_line > 0) { if (info->first_line == info->last_line) { if (info->first_column == info->last_column) { fprintf(out, "%d.%d:", info->first_line, info->first_column); } else { fprintf(out, "%d.%d-.%d:", info->first_line, info->first_column, info->last_column); } } else { fprintf(out, "%d.%d-%d.%d:", info->first_line, info->first_column, info->last_line, info->last_column); } } } bool typecheck_p(const struct info *info) { return (info->error->aug->flags & AUG_TYPE_CHECK) != 0; } void free_info(struct info *info) { if (info == NULL) return; assert(info->ref == 0); unref(info->filename, string); free(info); } struct span *make_span(struct info *info) { struct span *span = NULL; if (ALLOC(span) < 0) { return NULL; } /* UINT_MAX means span is not initialized yet */ span->span_start = UINT_MAX; span->filename = ref(info->filename); return span; } void free_span(struct span *span) { if (span == NULL) return; unref(span->filename, string); free(span); } void print_span(struct span *span) { if (span == NULL) return; printf("%s label=(%i:%i) value=(%i:%i) span=(%i,%i)\n", span->filename->str, span->label_start, span->label_end, span->value_start, span->value_end, span->span_start, span->span_end); } void update_span(struct span *node_info, int x, int y) { if (node_info == NULL) return; if (node_info->span_start == UINT_MAX) { node_info->span_start = x; node_info->span_end = y; } else { if (node_info->span_start > x) node_info->span_start = x; if (node_info->span_end < y) node_info->span_end = y; } } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/augeas.h0000644000175000017500000005616714161102026012177 00000000000000/* * augeas.h: public headers for augeas * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include #ifndef AUGEAS_H_ #define AUGEAS_H_ typedef struct augeas augeas; /* Enum: aug_flags * * Flags to influence the behavior of Augeas. Pass a bitmask of these flags * to AUG_INIT. */ enum aug_flags { AUG_NONE = 0, AUG_SAVE_BACKUP = (1 << 0), /* Keep the original file with a .augsave extension */ AUG_SAVE_NEWFILE = (1 << 1), /* Save changes into a file with extension .augnew, and do not overwrite the original file. Takes precedence over AUG_SAVE_BACKUP */ AUG_TYPE_CHECK = (1 << 2), /* Typecheck lenses; since it can be very expensive it is not done by default */ AUG_NO_STDINC = (1 << 3), /* Do not use the builtin load path for modules */ AUG_SAVE_NOOP = (1 << 4), /* Make save a no-op process, just record what would have changed */ AUG_NO_LOAD = (1 << 5), /* Do not load the tree from AUG_INIT */ AUG_NO_MODL_AUTOLOAD = (1 << 6), AUG_ENABLE_SPAN = (1 << 7), /* Track the span in the input of nodes */ AUG_NO_ERR_CLOSE = (1 << 8), /* Do not close automatically when encountering error during aug_init */ AUG_TRACE_MODULE_LOADING = (1 << 9) /* For use by augparse -t */ }; #ifdef __cplusplus extern "C" { #endif /* Function: aug_init * * Initialize the library. * * Use ROOT as the filesystem root. If ROOT is NULL, use the value of the * environment variable AUGEAS_ROOT. If that doesn't exist eitehr, use "/". * * LOADPATH is a colon-separated list of directories that modules should be * searched in. This is in addition to the standard load path and the * directories in AUGEAS_LENS_LIB. LOADPATH can be NULL, indicating that * nothing should be added to the load path. * * FLAGS is a bitmask made up of values from AUG_FLAGS. The flag * AUG_NO_ERR_CLOSE can be used to get more information on why * initialization failed. If it is set in FLAGS, the caller must check that * aug_error returns AUG_NOERROR before using the returned augeas handle * for any other operation. If the handle reports any error, the caller * should only call the aug_error functions an aug_close on this handle. * * Returns: * a handle to the Augeas tree upon success. If initialization fails, * returns NULL if AUG_NO_ERR_CLOSE is not set in FLAGS. If * AUG_NO_ERR_CLOSE is set, might return an Augeas handle even on * failure. In that case, caller must check for errors using augeas_error, * and, if an error is reported, only use the handle with the aug_error * functions and aug_close. */ augeas *aug_init(const char *root, const char *loadpath, unsigned int flags); /* Function: aug_defvar * * Define a variable NAME whose value is the result of evaluating EXPR. If * a variable NAME already exists, its name will be replaced with the * result of evaluating EXPR. Context will not be applied to EXPR. * * If EXPR is NULL, the variable NAME will be removed if it is defined. * * Path variables can be used in path expressions later on by prefixing * them with '$'. * * Returns -1 on error; on success, returns 0 if EXPR evaluates to anything * other than a nodeset, and the number of nodes if EXPR evaluates to a * nodeset */ int aug_defvar(augeas *aug, const char *name, const char *expr); /* Function: aug_defnode * * Define a variable NAME whose value is the result of evaluating EXPR, * which must be non-NULL and evaluate to a nodeset. If a variable NAME * already exists, its name will be replaced with the result of evaluating * EXPR. * * If EXPR evaluates to an empty nodeset, a node is created, equivalent to * calling AUG_SET(AUG, EXPR, VALUE) and NAME will be the nodeset containing * that single node. * * If CREATED is non-NULL, it is set to 1 if a node was created, and 0 if * it already existed. * * Returns -1 on error; on success, returns the number of nodes in the * nodeset */ int aug_defnode(augeas *aug, const char *name, const char *expr, const char *value, int *created); /* Function: aug_get * * Lookup the value associated with PATH. VALUE can be NULL, in which case * it is ignored. If VALUE is not NULL, it is used to return a pointer to * the value associated with PATH if PATH matches exactly one node. If PATH * matches no nodes or more than one node, *VALUE is set to NULL. Note that * it is perfectly legal for nodes to have a NULL value, and that that by * itself does not indicate an error. * * The string *VALUE must not be freed by the caller, and is valid as long * as its node remains unchanged. * * Returns: * 1 if there is exactly one node matching PATH, 0 if there is none, * and a negative value if there is more than one node matching PATH, or if * PATH is not a legal path expression. */ int aug_get(const augeas *aug, const char *path, const char **value); /* Function: aug_label * * Lookup the label associated with PATH. LABEL can be NULL, in which case * it is ignored. If LABEL is not NULL, it is used to return a pointer to * the value associated with PATH if PATH matches exactly one node. If PATH * matches no nodes or more than one node, *LABEL is set to NULL. * * The string *LABEL must not be freed by the caller, and is valid as long * as its node remains unchanged. * * Returns: * 1 if there is exactly one node matching PATH, 0 if there is none, * and a negative value if there is more than one node matching PATH, or if * PATH is not a legal path expression. */ int aug_label(const augeas *aug, const char *path, const char **label); /* Function: aug_set * * Set the value associated with PATH to VALUE. VALUE is copied into the * internal data structure, and the caller is responsible for freeing * it. Intermediate entries are created if they don't exist. * * Returns: * 0 on success, -1 on error. It is an error if more than one node * matches PATH. */ int aug_set(augeas *aug, const char *path, const char *value); /* Function: aug_setm * * Set the value of multiple nodes in one operation. Find or create a node * matching SUB by interpreting SUB as a path expression relative to each * node matching BASE. SUB may be NULL, in which case all the nodes * matching BASE will be modified. * * Returns: * number of modified nodes on success, -1 on error */ int aug_setm(augeas *aug, const char *base, const char *sub, const char *value); /* Function: aug_span * * Get the span according to input file of the node associated with PATH. If * the node is associated with a file, the filename, label and value start and * end positions are set, and return value is 0. The caller is responsible for * freeing returned filename. If an argument for return value is NULL, then the * corresponding value is not set. If the node associated with PATH doesn't * belong to a file or is doesn't exists, filename and span are not set and * return value is -1. * * Returns: * 0 on success with filename, label_start, label_stop, value_start, value_end, * span_start, span_end * -1 on error */ int aug_span(augeas *aug, const char *path, char **filename, unsigned int *label_start, unsigned int *label_end, unsigned int *value_start, unsigned int *value_end, unsigned int *span_start, unsigned int *span_end); /* Function: aug_insert * * Create a new sibling LABEL for PATH by inserting into the tree just * before PATH if BEFORE == 1 or just after PATH if BEFORE == 0. * * PATH must match exactly one existing node in the tree, and LABEL must be * a label, i.e. not contain a '/', '*' or end with a bracketed index * '[N]'. * * Returns: * 0 on success, and -1 if the insertion fails. */ int aug_insert(augeas *aug, const char *path, const char *label, int before); /* Function: aug_rm * * Remove path and all its children. Returns the number of entries removed. * All nodes that match PATH, and their descendants, are removed. */ int aug_rm(augeas *aug, const char *path); /* Function: aug_mv * * Move the node SRC to DST. SRC must match exactly one node in the * tree. DST must either match exactly one node in the tree, or may not * exist yet. If DST exists already, it and all its descendants are * deleted. If DST does not exist yet, it and all its missing ancestors are * created. * * Note that the node SRC always becomes the node DST: when you move /a/b * to /x, the node /a/b is now called /x, no matter whether /x existed * initially or not. * * Returns: * 0 on success and -1 on failure. */ int aug_mv(augeas *aug, const char *src, const char *dst); /* Function: aug_cp * * Copy the node SRC to DST. SRC must match exactly one node in the * tree. DST must either match exactly one node in the tree, or may not * exist yet. If DST exists already, it and all its descendants are * deleted. If DST does not exist yet, it and all its missing ancestors are * created. * * Returns: * 0 on success and -1 on failure. */ int aug_cp(augeas *aug, const char *src, const char *dst); /* Function: aug_rename * * Rename the label of all nodes matching SRC to LBL. * * Returns: * The number of nodes renamed on success and -1 on failure. */ int aug_rename(augeas *aug, const char *src, const char *lbl); /* Function: aug_match * * Returns: * the number of matches of the path expression PATH in AUG. If * MATCHES is non-NULL, an array with the returned number of elements will * be allocated and filled with the paths of the matches. The caller must * free both the array and the entries in it. The returned paths are * sufficiently qualified to make sure that they match exactly one node in * the current tree. * * If MATCHES is NULL, nothing is allocated and only the number * of matches is returned. * * Returns -1 on error, or the total number of matches (which might be 0). * * Path expressions: * Path expressions use a very simple subset of XPath: the path PATH * consists of a number of segments, separated by '/'; each segment can * either be a '*', matching any tree node, or a string, optionally * followed by an index in brackets, matching tree nodes labelled with * exactly that string. If no index is specified, the expression matches * all nodes with that label; the index can be a positive number N, which * matches exactly the Nth node with that label (counting from 1), or the * special expression 'last()' which matches the last node with the given * label. All matches are done in fixed positions in the tree, and nothing * matches more than one path segment. * */ int aug_match(const augeas *aug, const char *path, char ***matches); /* Function: aug_save * * Write all pending changes to disk. * * Returns: * -1 if an error is encountered, * 0 on success. Only files that had any changes made to them are written. * * If AUG_SAVE_NEWFILE is set in the FLAGS passed to AUG_INIT, create * changed files as new files with the extension ".augnew", and leave the * original file unmodified. * * Otherwise, if AUG_SAVE_BACKUP is set in the FLAGS passed to AUG_INIT, * move the original file to a new file with extension ".augsave". * * If neither of these flags is set, overwrite the original file. */ int aug_save(augeas *aug); /* Function: aug_load * * Load files into the tree. Which files to load and what lenses to use on * them is specified under /augeas/load in the tree; each entry * /augeas/load/NAME specifies a 'transform', by having itself exactly one * child 'lens' and any number of children labelled 'incl' and 'excl'. The * value of NAME has no meaning. * * The 'lens' grandchild of /augeas/load specifies which lens to use, and * can either be the fully qualified name of a lens 'Module.lens' or * '@Module'. The latter form means that the lens from the transform marked * for autoloading in MODULE should be used. * * The 'incl' and 'excl' grandchildren of /augeas/load indicate which files * to transform. Their value are used as glob patterns. Any file that * matches at least one 'incl' pattern and no 'excl' pattern is * transformed. The order of 'incl' and 'excl' entries is irrelevant. * * When AUG_INIT is first called, it populates /augeas/load with the * transforms marked for autoloading in all the modules it finds. * * Before loading any files, AUG_LOAD will remove everything underneath * /augeas/files and /files, regardless of whether any entries have been * modified or not. * * Returns -1 on error, 0 on success. Note that success includes the case * where some files could not be loaded. Details of such files can be found * as '/augeas//error'. */ int aug_load(augeas *aug); /* Function: aug_text_store * * Use the value of node NODE as a string and transform it into a tree * using the lens LENS and store it in the tree at PATH, which will be * overwritten. PATH and NODE are path expressions. * * Returns: * 0 on success, or a negative value on failure */ int aug_text_store(augeas *aug, const char *lens, const char *node, const char *path); /* Function: aug_text_retrieve * * Transform the tree at PATH into a string using lens LENS and store it in * the node NODE_OUT, assuming the tree was initially generated using the * value of node NODE_IN. PATH, NODE_IN, and NODE_OUT are path expressions. * * Returns: * 0 on success, or a negative value on failure */ int aug_text_retrieve(struct augeas *aug, const char *lens, const char *node_in, const char *path, const char *node_out); /* Function: aug_escape_name * * Escape special characters in a string such that it can be used as part * of a path expressions and only matches a node named exactly * IN. Characters that have special meanings in path expressions, such as * '[' and ']' are prefixed with a '\\'. Note that this function assumes * that it is passed a name, not a path, and will therefore escape '/', * too. * * On return, *OUT is NULL if IN does not need any escaping at all, and * points to an escaped copy of IN otherwise. * * Returns: * 0 on success, or a negative value on failure */ int aug_escape_name(augeas *aug, const char *in, char **out); /* Function: aug_print * * Print each node matching PATH and its descendants to OUT. * * Returns: * 0 on success, or a negative value on failure */ int aug_print(const augeas *aug, FILE *out, const char *path); /* Function: aug_source * * For the node matching PATH, return the path to the node representing the * file to which PATH belongs. If PATH belongs to a file, *FILE_PATH will * contain the path to the toplevel node of that file underneath /files. If * it does not, *FILE_PATH will be NULL. * * The caller is responsible for freeing *FILE_PATH * * Returns: * 0 on success, or a negative value on failure. It is an error if PATH * matches more than one node. */ int aug_source(const augeas *aug, const char *path, char **file_path); /* Function: aug_preview * * Return the contents of the file that would be written for the file associated with path * If there is no file corresponfing to PATH, *OUT will be NULL. * The caller is responsible for freeing *OUT * * Returns: * 0 on success, -1 on error */ int aug_preview(augeas *aug, const char *path, char **out); /* Function: aug_to_xml * * Turn the Augeas tree(s) matching PATH into an XML tree XMLDOC. The * parameter FLAGS is currently unused and must be set to 0. * * Returns: * 0 on success, or a negative value on failure * * In case of failure, *xmldoc is set to NULL */ int aug_to_xml(const augeas *aug, const char *path, xmlNode **xmldoc, unsigned int flags); /* * Function: aug_transform * * Add a transform for FILE using LENS. * EXCL specifies if this the file is to be included (0) * or excluded (1) from the LENS. * The LENS maybe be a module name or a full lens name. * If a module name is given, then lns will be the lens assumed. * * Returns: * 1 on success, -1 on failure */ int aug_transform(augeas *aug, const char *lens, const char *file, int excl); /* * Function: aug_load_file * * Load a FILE using the lens that would ordinarily be used by aug_load, * i.e. the lens whose autoload statement matches the FILE. Similar to * aug_load, this function returns successfully even if FILE does not exist * or if the FILE can not be processed by the associated lens. It is an * error though if no lens can be found to process FILE. In that case, the * error code in AUG will be set to AUG_ENOLENS. * * Returns: * 0 on success, -1 on failure */ int aug_load_file(augeas *aug, const char *file); /* * Function: aug_srun * * Run one or more newline-separated commands. The output of the commands * will be printed to OUT. Running just 'help' will print what commands are * available. Commands accepted by this are identical to what augtool * accepts. * * Returns: * the number of executed commands on success, -1 on failure, and -2 if a * 'quit' command was encountered */ int aug_srun(augeas *aug, FILE *out, const char *text); /* Function: aug_close * * Close this Augeas instance and free any storage associated with * it. After running AUG_CLOSE, AUG is invalid and can not be used for any * more operations. */ void aug_close(augeas *aug); // We can't put //* into the examples in these comments since the C // preprocessor complains about that. So we'll resort to the equivalent but // more wordy notation /descendant::* /* * Function: aug_ns_attr * * Look up the ith node in the variable VAR and retrieve information about * it. Set *VALUE to the value of the node, *LABEL to its label, and * *FILE_PATH to the path of the file it belongs to, or to NULL if that * node does not belong to a file. It is permissible to pass NULL for any * of these variables to indicate that the caller is not interested in that * attribute. * * It is assumed that VAR was defined with a path expression evaluating to * a nodeset, like '/files/etc/hosts/descendant::*'. This function is * equivalent to, but faster than, aug_get(aug, "$VAR[I+1]", value), * respectively the corresponding calls to aug_label and aug_source. Note * that the index is 0-based, not 1-based. * * If VAR does not exist, or is not a nodeset, or if it has fewer than I * nodes, this call fails. * * The caller is responsible for freeing *FILE_PATH, but must not free * *VALUE or *LABEL. Those pointers are only valid up to the next call to a * function in this API that might modify the tree. * * Returns: * 1 on success (for consistency with aug_get), a negative value on failure */ int aug_ns_attr(const augeas* aug, const char *var, int i, const char **value, const char **label, char **file_path); /* * Function: aug_ns_label * * Look up the LABEL and its INDEX amongst its siblings for the ith node in * variable VAR. (See aug_ns_attr for details of what is expected of VAR) * * Either of LABEL and INDEX may be NULL. The *INDEX will be set to the * number of siblings + 1 of the node $VAR[I+1] that precede it and have * the same label if there are at least two siblings with that label. If * the node $VAR[I+1] does not have any siblings with the same label as * itself, *INDEX will be set to 0. * * The caller must not free *LABEL. The pointer is only valid up to the * next call to a function in this API that might modify the tree. * * Returns: * 1 on success (for consistency with aug_get), a negative value on failure */ int aug_ns_label(const augeas *aug, const char *var, int i, const char **label, int *index); /* * Function: aug_ns_value * * Look up the VALUE of the ith node in variable VAR. (See aug_ns_attr for * details of what is expected of VAR) * * The caller must not free *VALUE. The pointer is only valid up to the * next call to a function in this API that might modify the tree. * * Returns: * 1 on success (for consistency with aug_get), a negative value on failure */ int aug_ns_value(const augeas *aug, const char *var, int i, const char **value); /* * Function: aug_ns_count * * Return the number of nodes in variable VAR. (See aug_ns_attr for details * of what is expected of VAR) * * Returns: the number of nodes in VAR, or a negative value on failure */ int aug_ns_count(const augeas *aug, const char *var); /* * Function: aug_ns_count * * Put the fully qualified path to the ith node in VAR into *PATH. (See * aug_ns_attr for details of what is expected of VAR) * * The caller is responsible for freeing *PATH, which is allocated by this * function. * * Returns: 1 on success (for consistency with aug_get), a negative value * on failure */ int aug_ns_path(const augeas *aug, const char *var, int i, char **path); /* * Error reporting */ typedef enum { AUG_NOERROR, /* No error */ AUG_ENOMEM, /* Out of memory */ AUG_EINTERNAL, /* Internal error (bug) */ AUG_EPATHX, /* Invalid path expression */ AUG_ENOMATCH, /* No match for path expression */ AUG_EMMATCH, /* Too many matches for path expression */ AUG_ESYNTAX, /* Syntax error in lens file */ AUG_ENOLENS, /* Lens lookup failed */ AUG_EMXFM, /* Multiple transforms */ AUG_ENOSPAN, /* No span for this node */ AUG_EMVDESC, /* Cannot move node into its descendant */ AUG_ECMDRUN, /* Failed to execute command */ AUG_EBADARG, /* Invalid argument in function call */ AUG_ELABEL, /* Invalid label */ AUG_ECPDESC, /* Cannot copy node into its descendant */ AUG_EFILEACCESS /* Cannot open or read a file */ } aug_errcode_t; /* Return the error code from the last API call */ int aug_error(augeas *aug); /* Return a human-readable message for the error code */ const char *aug_error_message(augeas *aug); /* Return a human-readable message elaborating the error code; might be * NULL. For example, when the error code is AUG_EPATHX, this will explain * how the path expression is invalid */ const char *aug_error_minor_message(augeas *aug); /* Return details about the error, which might be NULL. For example, for * AUG_EPATHX, indicates where in the path expression the error * occurred. The returned value can only be used until the next API call */ const char *aug_error_details(augeas *aug); #ifdef __cplusplus } #endif #endif /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/memory.h0000644000175000017500000000511014161102026012220 00000000000000/* * memory.c: safer memory allocation * * Copyright (C) 2008-2016 Daniel P. Berrange * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef MEMORY_H_ #define MEMORY_H_ #include "internal.h" /* Don't call these directly - use the macros below */ int mem_alloc_n(void *ptrptr, size_t size, size_t count) ATTRIBUTE_RETURN_CHECK; int mem_realloc_n(void *ptrptr, size_t size, size_t count) ATTRIBUTE_RETURN_CHECK; /** * ALLOC: * @ptr: pointer to hold address of allocated memory * * Allocate sizeof(*ptr) bytes of memory and store * the address of allocated memory in 'ptr'. Fill the * newly allocated memory with zeros. * * Returns -1 on failure, 0 on success */ #define ALLOC(ptr) mem_alloc_n(&(ptr), sizeof(*(ptr)), 1) /** * ALLOC_N: * @ptr: pointer to hold address of allocated memory * @count: number of elements to allocate * * Allocate an array of 'count' elements, each sizeof(*ptr) * bytes long and store the address of allocated memory in * 'ptr'. Fill the newly allocated memory with zeros. * * Returns -1 on failure, 0 on success */ #define ALLOC_N(ptr, count) mem_alloc_n(&(ptr), sizeof(*(ptr)), (count)) /** * REALLOC_N: * @ptr: pointer to hold address of allocated memory * @count: number of elements to allocate * * Re-allocate an array of 'count' elements, each sizeof(*ptr) * bytes long and store the address of allocated memory in * 'ptr'. Fill the newly allocated memory with zeros * * Returns -1 on failure, 0 on success */ #define REALLOC_N(ptr, count) mem_realloc_n(&(ptr), sizeof(*(ptr)), (count)) /** * FREE: * @ptr: pointer holding address to be freed * * Free the memory stored in 'ptr' and update to point * to NULL. */ #define FREE(ptr) \ do { \ free(ptr); \ (ptr) = NULL; \ } while(0) #endif /* __VIR_MEMORY_H_ */ augeas-1.13.0/src/augrun.c0000644000175000017500000014664714161102026012231 00000000000000/* * augrun.c: command interpreter for augeas * * Copyright (C) 2011-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include "augeas.h" #include "internal.h" #include "memory.h" #include "errcode.h" #include #include #include /* * Command handling infrastructure */ enum command_opt_type { CMD_NONE, CMD_STR, /* String argument */ CMD_PATH /* Path expression */ }; struct command_opt_def { bool optional; /* Optional or mandatory */ enum command_opt_type type; const char *name; const char *help; }; #define CMD_OPT_DEF_LAST { .type = CMD_NONE, .name = NULL } struct command { const struct command_def *def; struct command_opt *opt; struct augeas *aug; struct error *error; /* Same as aug->error */ FILE *out; bool quit; }; typedef void (*cmd_handler)(struct command*); struct command_def { const char *name; const char *category; const struct command_opt_def *opts; cmd_handler handler; const char *synopsis; const char *help; }; static const struct command_def cmd_def_last = { .name = NULL, .opts = NULL, .handler = NULL, .synopsis = NULL, .help = NULL }; struct command_opt { struct command_opt *next; const struct command_opt_def *def; char *value; }; struct command_grp_def { const char *name; const struct command_def *commands[]; }; static const struct command_grp_def cmd_grp_def_last = { .name = NULL, .commands = { } }; static const struct command_def *lookup_cmd_def(const char *name); static const struct command_opt_def * find_def(const struct command *cmd, const char *name) { const struct command_opt_def *def; for (def = cmd->def->opts; def->name != NULL; def++) { if (STREQ(def->name, name)) return def; } return NULL; } static struct command_opt * find_opt(const struct command *cmd, const char *name) { const struct command_opt_def *def = find_def(cmd, name); assert(def != NULL); for (struct command_opt *opt = cmd->opt; opt != NULL; opt = opt->next) { if (opt->def == def) return opt; } assert(def->optional); return NULL; } static const char *arg_value(const struct command *cmd, const char *name) { struct command_opt *opt = find_opt(cmd, name); return (opt == NULL) ? NULL : opt->value; } static char *nexttoken(struct command *cmd, char **line, bool path) { char *r, *s, *w; char quot = '\0'; int nbracket = 0; int nescaped = 0; bool copy; s = *line; while (*s && isblank(*s)) s+= 1; r = s; w = s; while (*s) { copy = true; if (*s == '\\') { switch (*(s+1)) { case ']': case '[': case '|': case '/': case '=': case '(': case ')': case '!': case ',': /* pass them literally; * see 'name_follow' in pathx.c */ nescaped = 2; break; case 't': /* insert tab */ *(s+1) = '\t'; nescaped = 1; s += 1; break; case 'n': /* insert newline */ *(s+1) = '\n'; nescaped = 1; s += 1; break; case ' ': case '\t': /* pass both through if quoted, else fall */ if (quot) break; ATTRIBUTE_FALLTHROUGH; case '\'': case '"': /* pass both through if opposite quote, else fall */ if (quot && quot != *(s+1)) break; ATTRIBUTE_FALLTHROUGH; case '\\': /* pass next character through */ nescaped = 1; s += 1; break; default: ERR_REPORT(cmd, AUG_ECMDRUN, "unknown escape sequence: %c", *(s+1)); return NULL; } } if (nescaped == 0) { if (*s == '[') nbracket += 1; if (*s == ']') nbracket -= 1; if (nbracket < 0) { ERR_REPORT(cmd, AUG_ECMDRUN, "unmatched ["); return NULL; } if (!path || nbracket == 0) { if (!quot && (*s == '\'' || *s == '"')) { quot = *s; copy = false; } else if (quot && *s == quot) { quot = '\0'; copy = false; } if (!quot && isblank(*s)) break; } } else { nescaped -= 1; } if (copy) { *w = *s; w += 1; } s += 1; } if (*s == '\0' && path && nbracket > 0) { ERR_REPORT(cmd, AUG_ECMDRUN, "unmatched ["); return NULL; } if (*s == '\0' && quot) { ERR_REPORT(cmd, AUG_ECMDRUN, "unmatched %c", quot); return NULL; } while (*w && w <= s) *w++ = '\0'; *line = w; return r; } static struct command_opt * make_command_opt(struct command *cmd, const struct command_opt_def *def) { struct command_opt *copt = NULL; int r; r = ALLOC(copt); ERR_NOMEM(r < 0, cmd->aug); copt->def = def; list_append(cmd->opt, copt); error: return copt; } static void free_command_opts(struct command *cmd) { struct command_opt *next; next = cmd->opt; while (next != NULL) { struct command_opt *del = next; next = del->next; free(del); } cmd->opt = NULL; } static int parseline(struct command *cmd, char *line) { char *tok; int narg = 0, nopt = 0; const struct command_opt_def *def; tok = nexttoken(cmd, &line, false); if (tok == NULL) return -1; cmd->def = lookup_cmd_def(tok); if (cmd->def == NULL) { ERR_REPORT(cmd, AUG_ECMDRUN, "Unknown command '%s'", tok); return -1; } for (def = cmd->def->opts; def->name != NULL; def++) { narg += 1; if (def->optional) nopt += 1; } int curarg = 0; def = cmd->def->opts; while (*line != '\0') { while (*line && isblank(*line)) line += 1; if (curarg >= narg) { ERR_REPORT(cmd, AUG_ECMDRUN, "Too many arguments. Command %s takes only %d arguments", cmd->def->name, narg); return -1; } struct command_opt *opt = make_command_opt(cmd, def); if (opt == NULL) return -1; if (def->type == CMD_PATH) { tok = nexttoken(cmd, &line, true); cleanpath(tok); } else { tok = nexttoken(cmd, &line, false); } if (tok == NULL) return -1; opt->value = tok; curarg += 1; def += 1; while (*line && isblank(*line)) line += 1; } if (curarg < narg - nopt) { ERR_REPORT(cmd, AUG_ECMDRUN, "Not enough arguments for %s", cmd->def->name); return -1; } return 0; } /* * Commands */ static void format_desc(const char *d) { printf(" "); for (const char *s = d; *s; s++) { if (*s == '\n') printf("\n "); else putchar(*s); } printf("\n\n"); } static void format_defname(char *buf, const struct command_opt_def *def, bool mark_optional) { char *p; if (mark_optional && def->optional) p = stpcpy(buf, " [<"); else p = stpcpy(buf, " <"); for (int i=0; i < strlen(def->name); i++) *p++ = toupper(def->name[i]); *p++ = '>'; if (mark_optional && def->optional) *p++ = ']'; *p = '\0'; } static void cmd_help(struct command *cmd); static const struct command_opt_def cmd_help_opts[] = { { .type = CMD_STR, .name = "command", .optional = true, .help = "print help for this command only" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_help_def = { .name = "help", .opts = cmd_help_opts, .handler = cmd_help, .synopsis = "print help", .help = "list all commands or print details about one command" }; static void cmd_quit(ATTRIBUTE_UNUSED struct command *cmd) { cmd->quit = true; } static const struct command_opt_def cmd_quit_opts[] = { CMD_OPT_DEF_LAST }; static const struct command_def cmd_quit_def = { .name = "quit", .opts = cmd_quit_opts, .handler = cmd_quit, .synopsis = "exit the program", .help = "Exit the program" }; static char *ls_pattern(struct command *cmd, const char *path) { char *q = NULL; int r; if (path[strlen(path)-1] == SEP) r = xasprintf(&q, "%s*", path); else r = xasprintf(&q, "%s/*", path); ERR_NOMEM(r < 0, cmd->aug); error: return q; } static int child_count(struct command *cmd, const char *path) { char *q = ls_pattern(cmd, path); int cnt; if (q == NULL) return 0; cnt = aug_match(cmd->aug, q, NULL); if (HAS_ERR(cmd)) cnt = -1; free(q); return cnt; } static void cmd_ls(struct command *cmd) { int cnt = 0; char *path = NULL; char **paths = NULL; path = ls_pattern(cmd, arg_value(cmd, "path")); ERR_BAIL(cmd); cnt = aug_match(cmd->aug, path, &paths); ERR_BAIL(cmd); for (int i=0; i < cnt; i++) { const char *val; const char *basnam = strrchr(paths[i], SEP); int dir = child_count(cmd, paths[i]); aug_get(cmd->aug, paths[i], &val); ERR_BAIL(cmd); basnam = (basnam == NULL) ? paths[i] : basnam + 1; if (val == NULL) val = "(none)"; fprintf(cmd->out, "%s%s= %s\n", basnam, dir ? "/ " : " ", val); FREE(paths[i]); } error: free(path); for (int i=0; i < cnt; i++) FREE(paths[i]); free(paths); } static const struct command_opt_def cmd_ls_opts[] = { { .type = CMD_PATH, .name = "path", .optional = false, .help = "the node whose children to list" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_ls_def = { .name = "ls", .opts = cmd_ls_opts, .handler = cmd_ls, .synopsis = "list children of a node", .help = "list the direct children of a node" }; static void cmd_match(struct command *cmd) { int cnt = 0; const char *pattern = arg_value(cmd, "path"); const char *value = arg_value(cmd, "value"); char **matches = NULL; bool filter = (value != NULL) && (strlen(value) > 0); cnt = aug_match(cmd->aug, pattern, &matches); ERR_BAIL(cmd); ERR_THROW(cnt < 0, cmd->aug, AUG_ECMDRUN, " (error matching %s)\n", pattern); if (cnt == 0) { fprintf(cmd->out, " (no matches)\n"); goto done; } for (int i=0; i < cnt; i++) { const char *val; aug_get(cmd->aug, matches[i], &val); ERR_BAIL(cmd); if (val == NULL) val = "(none)"; if (filter) { if (STREQ(value, val)) fprintf(cmd->out, "%s\n", matches[i]); } else { fprintf(cmd->out, "%s = %s\n", matches[i], val); } } error: done: for (int i=0; i < cnt; i++) free(matches[i]); free(matches); } static const struct command_opt_def cmd_match_opts[] = { { .type = CMD_PATH, .name = "path", .optional = false, .help = "the path expression to match" }, { .type = CMD_STR, .name = "value", .optional = true, .help = "only show matches with this value" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_match_def = { .name = "match", .opts = cmd_match_opts, .handler = cmd_match, .synopsis = "print matches for a path expression", .help = "Find all paths that match the path expression PATH. " "If VALUE is given,\n only the matching paths whose value equals " "VALUE are printed" }; static void cmd_count(struct command *cmd) { int cnt = 0; const char *pattern = arg_value(cmd, "path"); cnt = aug_match(cmd->aug, pattern, NULL); ERR_BAIL(cmd); ERR_THROW(cnt < 0, cmd->aug, AUG_ECMDRUN, " (error matching %s)\n", pattern); if (cnt == 0) { fprintf(cmd->out, " no matches\n"); } else if (cnt == 1) { fprintf(cmd->out, " 1 match\n"); } else { fprintf(cmd->out, " %d matches\n", cnt); } error: return; } static const struct command_opt_def cmd_count_opts[] = { { .type = CMD_PATH, .name = "path", .optional = false, .help = "the path expression to match" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_count_def = { .name = "count", .opts = cmd_count_opts, .handler = cmd_count, .synopsis = "print the number of matches for a path expression", .help = "Print how many paths match the the path expression PATH" }; static void cmd_rm(struct command *cmd) { int cnt; const char *path = arg_value(cmd, "path"); cnt = aug_rm(cmd->aug, path); if (! HAS_ERR(cmd)) fprintf(cmd->out, "rm : %s %d\n", path, cnt); } static const struct command_opt_def cmd_rm_opts[] = { { .type = CMD_PATH, .name = "path", .optional = false, .help = "remove all nodes matching this path expression" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_rm_def = { .name = "rm", .opts = cmd_rm_opts, .handler = cmd_rm, .synopsis = "delete nodes and subtrees", .help = "Delete PATH and all its children from the tree" }; static void cmd_mv(struct command *cmd) { const char *src = arg_value(cmd, "src"); const char *dst = arg_value(cmd, "dst"); int r; r = aug_mv(cmd->aug, src, dst); if (r < 0) ERR_REPORT(cmd, AUG_ECMDRUN, "Moving %s to %s failed", src, dst); } static const struct command_opt_def cmd_mv_opts[] = { { .type = CMD_PATH, .name = "src", .optional = false, .help = "the tree to move" }, { .type = CMD_PATH, .name = "dst", .optional = false, .help = "where to put the source tree" }, CMD_OPT_DEF_LAST }; static const char cmd_mv_help[] = "Move node SRC to DST. SRC must match exactly one node in " "the tree.\n DST must either match exactly one node in the tree, " "or may not\n exist yet. If DST exists already, it and all its " "descendants are\n deleted. If DST does not exist yet, it and " "all its missing\n ancestors are created."; static const struct command_def cmd_mv_def = { .name = "mv", .opts = cmd_mv_opts, .handler = cmd_mv, .synopsis = "move a subtree", .help = cmd_mv_help }; static const struct command_def cmd_move_def = { .name = "move", .opts = cmd_mv_opts, .handler = cmd_mv, .synopsis = "move a subtree (alias of 'mv')", .help = cmd_mv_help }; static void cmd_cp(struct command *cmd) { const char *src = arg_value(cmd, "src"); const char *dst = arg_value(cmd, "dst"); int r; r = aug_cp(cmd->aug, src, dst); if (r < 0) ERR_REPORT(cmd, AUG_ECMDRUN, "Copying %s to %s failed", src, dst); } static const struct command_opt_def cmd_cp_opts[] = { { .type = CMD_PATH, .name = "src", .optional = false, .help = "the tree to copy" }, { .type = CMD_PATH, .name = "dst", .optional = false, .help = "where to copy the source tree" }, CMD_OPT_DEF_LAST }; static const char cmd_cp_help[] = "Copy node SRC to DST. SRC must match exactly one node in " "the tree.\n DST must either match exactly one node in the tree, " "or may not\n exist yet. If DST exists already, it and all its " "descendants are\n deleted. If DST does not exist yet, it and " "all its missing\n ancestors are created."; static const struct command_def cmd_cp_def = { .name = "cp", .opts = cmd_cp_opts, .handler = cmd_cp, .synopsis = "copy a subtree", .help = cmd_cp_help }; static const struct command_def cmd_copy_def = { .name = "copy", .opts = cmd_cp_opts, .handler = cmd_cp, .synopsis = "copy a subtree (alias of 'cp')", .help = cmd_cp_help }; static void cmd_rename(struct command *cmd) { const char *src = arg_value(cmd, "src"); const char *lbl = arg_value(cmd, "lbl"); int cnt; cnt = aug_rename(cmd->aug, src, lbl); if (cnt < 0) ERR_REPORT(cmd, AUG_ECMDRUN, "Renaming %s to %s failed", src, lbl); if (! HAS_ERR(cmd)) fprintf(cmd->out, "rename : %s to %s %d\n", src, lbl, cnt); } static const struct command_opt_def cmd_rename_opts[] = { { .type = CMD_PATH, .name = "src", .optional = false, .help = "the tree to rename" }, { .type = CMD_STR, .name = "lbl", .optional = false, .help = "the new label" }, CMD_OPT_DEF_LAST }; static const char cmd_rename_help[] = "Rename the label of all nodes matching SRC to LBL."; static const struct command_def cmd_rename_def = { .name = "rename", .opts = cmd_rename_opts, .handler = cmd_rename, .synopsis = "rename a subtree label", .help = cmd_rename_help }; static void cmd_set(struct command *cmd) { const char *path = arg_value(cmd, "path"); const char *val = arg_value(cmd, "value"); int r; r = aug_set(cmd->aug, path, val); if (r < 0) ERR_REPORT(cmd, AUG_ECMDRUN, "Setting %s failed", path); } static const struct command_opt_def cmd_set_opts[] = { { .type = CMD_PATH, .name = "path", .optional = false, .help = "set the value of this node" }, { .type = CMD_STR, .name = "value", .optional = true, .help = "the new value for the node" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_set_def = { .name = "set", .opts = cmd_set_opts, .handler = cmd_set, .synopsis = "set the value of a node", .help = "Associate VALUE with PATH. If PATH is not in the tree yet, " "it and all\n its ancestors will be created. These new tree entries " "will appear last\n amongst their siblings" }; static void cmd_setm(struct command *cmd) { const char *base = arg_value(cmd, "base"); const char *sub = arg_value(cmd, "sub"); const char *val = arg_value(cmd, "value"); aug_setm(cmd->aug, base, sub, val); } static const struct command_opt_def cmd_setm_opts[] = { { .type = CMD_PATH, .name = "base", .optional = false, .help = "the base node" }, { .type = CMD_PATH, .name = "sub", .optional = false, .help = "the subtree relative to the base" }, { .type = CMD_STR, .name = "value", .optional = true, .help = "the value for the nodes" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_setm_def = { .name = "setm", .opts = cmd_setm_opts, .handler = cmd_setm, .synopsis = "set the value of multiple nodes", .help = "Set multiple nodes in one operation. Find or create a node" " matching SUB\n by interpreting SUB as a path expression relative" " to each node matching\n BASE. If SUB is '.', the nodes matching " "BASE will be modified." }; static void cmd_clearm(struct command *cmd) { const char *base = arg_value(cmd, "base"); const char *sub = arg_value(cmd, "sub"); aug_setm(cmd->aug, base, sub, NULL); } static const struct command_opt_def cmd_clearm_opts[] = { { .type = CMD_PATH, .name = "base", .optional = false, .help = "the base node" }, { .type = CMD_PATH, .name = "sub", .optional = false, .help = "the subtree relative to the base" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_clearm_def = { .name = "clearm", .opts = cmd_clearm_opts, .handler = cmd_clearm, .synopsis = "clear the value of multiple nodes", .help = "Clear multiple nodes values in one operation. Find or create a" " node matching SUB\n by interpreting SUB as a path expression relative" " to each node matching\n BASE. If SUB is '.', the nodes matching " "BASE will be modified." }; static void cmd_span(struct command *cmd) { const char *path = arg_value(cmd, "path"); int r; uint label_start, label_end, value_start, value_end, span_start, span_end; char *filename = NULL; const char *option = NULL; if (aug_get(cmd->aug, AUGEAS_SPAN_OPTION, &option) != 1) { printf("Error: option " AUGEAS_SPAN_OPTION " not found\n"); return; } if (streqv(AUG_DISABLE, option)) { ERR_REPORT(cmd, AUG_ECMDRUN, "Span is not enabled. To enable, run the commands:\n" " set %s %s\n rm %s\n load\n", AUGEAS_SPAN_OPTION, AUG_ENABLE, AUGEAS_FILES_TREE); return; } else if (! streqv(AUG_ENABLE, option)) { ERR_REPORT(cmd, AUG_ECMDRUN, "option %s must be %s or %s\n", AUGEAS_SPAN_OPTION, AUG_ENABLE, AUG_DISABLE); return; } r = aug_span(cmd->aug, path, &filename, &label_start, &label_end, &value_start, &value_end, &span_start, &span_end); ERR_THROW(r == -1, cmd, AUG_ECMDRUN, "failed to retrieve span"); fprintf(cmd->out, "%s label=(%i:%i) value=(%i:%i) span=(%i,%i)\n", filename, label_start, label_end, value_start, value_end, span_start, span_end); error: free(filename); } static const struct command_opt_def cmd_span_opts[] = { { .type = CMD_PATH, .name = "path", .optional = false, .help = "path matching exactly one node" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_span_def = { .name = "span", .opts = cmd_span_opts, .handler = cmd_span, .synopsis = "print position in input file corresponding to tree", .help = "Print the name of the file from which the node PATH was generated, as\n well as information about the positions in the file corresponding to\n the label, the value, and the entire node. PATH must match exactly\n one node.\n\n You need to run 'set /augeas/span enable' prior to loading files to\n enable recording of span information. It is disabled by default." }; static void cmd_defvar(struct command *cmd) { const char *name = arg_value(cmd, "name"); const char *path = arg_value(cmd, "expr"); aug_defvar(cmd->aug, name, path); } static const struct command_opt_def cmd_defvar_opts[] = { { .type = CMD_STR, .name = "name", .optional = false, .help = "the name of the variable" }, { .type = CMD_PATH, .name = "expr", .optional = false, .help = "the path expression" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_defvar_def = { .name = "defvar", .opts = cmd_defvar_opts, .handler = cmd_defvar, .synopsis = "set a variable", .help = "Evaluate EXPR and set the variable NAME to the resulting " "nodeset. The\n variable can be used in path expressions as $NAME. " "Note that EXPR is\n evaluated when the variable is defined, not when " "it is used." }; static void cmd_defnode(struct command *cmd) { const char *name = arg_value(cmd, "name"); const char *path = arg_value(cmd, "expr"); const char *value = arg_value(cmd, "value"); /* Make 'defnode foo ""' mean the same as 'defnode foo' */ if (value != NULL && strlen(value) == 0) value = NULL; aug_defnode(cmd->aug, name, path, value, NULL); } static const struct command_opt_def cmd_defnode_opts[] = { { .type = CMD_STR, .name = "name", .optional = false, .help = "the name of the variable" }, { .type = CMD_PATH, .name = "expr", .optional = false, .help = "the path expression" }, { .type = CMD_STR, .name = "value", .optional = true, .help = "the value for the new node" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_defnode_def = { .name = "defnode", .opts = cmd_defnode_opts, .handler = cmd_defnode, .synopsis = "set a variable, possibly creating a new node", .help = "Define the variable NAME to the result of evaluating EXPR, " " which must\n be a nodeset. If no node matching EXPR exists yet, one " "is created and\n NAME will refer to it. When a node is created and " "VALUE is given, the\n new node's value is set to VALUE." }; static void cmd_clear(struct command *cmd) { const char *path = arg_value(cmd, "path"); int r; r = aug_set(cmd->aug, path, NULL); if (r < 0) ERR_REPORT(cmd, AUG_ECMDRUN, "Clearing %s failed", path); } static const struct command_opt_def cmd_clear_opts[] = { { .type = CMD_PATH, .name = "path", .optional = false, .help = "clear the value of this node" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_clear_def = { .name = "clear", .opts = cmd_clear_opts, .handler = cmd_clear, .synopsis = "clear the value of a node", .help = "Set the value for PATH to NULL. If PATH is not in the tree yet, " "it and\n all its ancestors will be created. These new tree entries " "will appear\n last amongst their siblings" }; static void cmd_touch(struct command *cmd) { const char *path = arg_value(cmd, "path"); int r; r = aug_match(cmd->aug, path, NULL); if (r == 0) { r = aug_set(cmd->aug, path, NULL); if (r < 0) ERR_REPORT(cmd, AUG_ECMDRUN, "Touching %s failed", path); } } static const struct command_opt_def cmd_touch_opts[] = { { .type = CMD_PATH, .name = "path", .optional = false, .help = "touch this node" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_touch_def = { .name = "touch", .opts = cmd_touch_opts, .handler = cmd_touch, .synopsis = "create a new node", .help = "Create PATH with the value NULL if it is not in the tree yet. " "All its\n ancestors will also be created. These new tree entries will " "appear\n last amongst their siblings." }; static void cmd_get(struct command *cmd) { const char *path = arg_value(cmd, "path"); const char *val; int r; r = aug_get(cmd->aug, path, &val); ERR_RET(cmd); fprintf(cmd->out, "%s", path); if (r == 0) { fprintf(cmd->out, " (o)\n"); } else if (val == NULL) { fprintf(cmd->out, " (none)\n"); } else { fprintf(cmd->out, " = %s\n", val); } } static const struct command_opt_def cmd_get_opts[] = { { .type = CMD_PATH, .name = "path", .optional = false, .help = "get the value of this node" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_get_def = { .name = "get", .opts = cmd_get_opts, .handler = cmd_get, .synopsis = "get the value of a node", .help = "Get and print the value associated with PATH" }; static void cmd_label(struct command *cmd) { const char *path = arg_value(cmd, "path"); const char *lbl; int r; r = aug_label(cmd->aug, path, &lbl); ERR_RET(cmd); fprintf(cmd->out, "%s", path); if (r == 0) { fprintf(cmd->out, " (o)\n"); } else if (lbl == NULL) { fprintf(cmd->out, " (none)\n"); } else { fprintf(cmd->out, " = %s\n", lbl); } } static const struct command_opt_def cmd_label_opts[] = { { .type = CMD_PATH, .name = "path", .optional = false, .help = "get the label of this node" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_label_def = { .name = "label", .opts = cmd_label_opts, .handler = cmd_label, .synopsis = "get the label of a node", .help = "Get and print the label associated with PATH" }; static void cmd_print(struct command *cmd) { const char *path = arg_value(cmd, "path"); aug_print(cmd->aug, cmd->out, path); } static const struct command_opt_def cmd_print_opts[] = { { .type = CMD_PATH, .name = "path", .optional = true, .help = "print this subtree" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_print_def = { .name = "print", .opts = cmd_print_opts, .handler = cmd_print, .synopsis = "print a subtree", .help = "Print entries in the tree. If PATH is given, printing starts there,\n otherwise the whole tree is printed" }; static void cmd_source(struct command *cmd) { const char *path = arg_value(cmd, "path"); char *file_path = NULL; aug_source(cmd->aug, path, &file_path); ERR_RET(cmd); if (file_path != NULL) { fprintf(cmd->out, "%s\n", file_path); } free(file_path); } static const struct command_opt_def cmd_source_opts[] = { { .type = CMD_PATH, .name = "path", .optional = false, .help = "path to a single node" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_source_def = { .name = "source", .opts = cmd_source_opts, .handler = cmd_source, .synopsis = "print the file to which a node belongs", .help = "Print the file to which the node for PATH belongs. PATH must match\n a single node coming from some file. In particular, that means\n it must be underneath /files." }; static void cmd_preview(struct command *cmd) { const char *path = arg_value(cmd, "path"); char *out = NULL; int r; r = aug_preview(cmd->aug, path, &out); if (r < 0 || out == NULL) ERR_REPORT(cmd, AUG_ECMDRUN, "Preview of file for path %s failed", path); else { fprintf(cmd->out, "%s", out); } free(out); } static const struct command_opt_def cmd_preview_opts[] = { { .type = CMD_PATH, .name = "path", .optional = false, .help = "preview the file output which corresponds to path" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_preview_def = { .name = "preview", .opts = cmd_preview_opts, .handler = cmd_preview, .synopsis = "preview the file contents for the path specified", .help = "Print the file that would be written, for the file that corresponds to a path." "\n The path must be within the " AUGEAS_FILES_TREE " tree." }; static void cmd_context(struct command *cmd) { const char *path = arg_value(cmd, "path"); if (path == NULL) { aug_get(cmd->aug, "/augeas/context", &path); ERR_RET(cmd); if (path == NULL) { fprintf(cmd->out, "/\n"); } else { fprintf(cmd->out, "%s\n", path); } } else { aug_set(cmd->aug, "/augeas/context", path); ERR_RET(cmd); } } static const struct command_opt_def cmd_context_opts[] = { { .type = CMD_PATH, .name = "path", .optional = true, .help = "new context for relative paths" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_context_def = { .name = "context", .opts = cmd_context_opts, .handler = cmd_context, .synopsis = "change how relative paths are interpreted", .help = "Relative paths are interpreted relative to a context path which\n is stored in /augeas/context.\n\n When no PATH is given, this command prints the current context path\n and is equivalent to 'get /augeas/context'\n\n When PATH is given, this command changes that context, and has a\n similar effect to 'cd' in a shell and and is the same as running\n the command 'set /augeas/context PATH'." }; static void cmd_dump_xml(struct command *cmd) { const char *path = arg_value(cmd, "path"); xmlNodePtr xmldoc; int r; r = aug_to_xml(cmd->aug, path, &xmldoc, 0); if (r < 0) ERR_REPORT(cmd, AUG_ECMDRUN, "XML export of path %s failed", path); xmlElemDump(stdout, NULL, xmldoc); printf("\n"); xmlFreeNode(xmldoc); } static const struct command_opt_def cmd_dump_xml_opts[] = { { .type = CMD_PATH, .name = "path", .optional = true, .help = "print this subtree" }, CMD_OPT_DEF_LAST }; static const struct command_def cmd_dump_xml_def = { .name = "dump-xml", .opts = cmd_dump_xml_opts, .handler = cmd_dump_xml, .synopsis = "print a subtree as XML", .help = "Export entries in the tree as XML. If PATH is given, printing starts there,\n otherwise the whole tree is printed." }; static void cmd_transform(struct command *cmd) { const char *lens = arg_value(cmd, "lens"); const char *filter = arg_value(cmd, "filter"); const char *file = arg_value(cmd, "file"); int r, excl = 0; if (STREQ("excl", filter)) excl = 1; else if (STREQ("incl", filter)) excl = 0; else ERR_REPORT(cmd, AUG_ECMDRUN, "FILTER must be \"incl\" or \"excl\""); r = aug_transform(cmd->aug, lens, file, excl); if (r < 0) ERR_REPORT(cmd, AUG_ECMDRUN, "Adding transform for %s on lens %s failed", lens, file); } static const struct command_opt_def cmd_transform_opts[] = { { .type = CMD_PATH, .name = "lens", .optional = false, .help = "the lens to use" }, { .type = CMD_PATH, .name = "filter", .optional = false, .help = "the type of filter, either \"incl\" or \"excl\"" }, { .type = CMD_PATH, .name = "file", .optional = false, .help = "the file to associate to the lens" }, CMD_OPT_DEF_LAST }; static const char cmd_transform_help[] = "Add a transform for FILE using LENS. The LENS may be a module name or a\n" " full lens name. If a module name is given, then \"lns\" will be the lens\n" " assumed. The FILTER must be either \"incl\" or \"excl\". If the filter is\n" " \"incl\", the FILE will be parsed by the LENS. If the filter is \"excl\",\n" " the FILE will be excluded from the LENS. FILE may contain wildcards." ; static const struct command_def cmd_transform_def = { .name = "transform", .opts = cmd_transform_opts, .handler = cmd_transform, .synopsis = "add a file transform", .help = cmd_transform_help }; static void cmd_load_file(struct command *cmd) { const char *file = arg_value(cmd, "file"); int r = 0; r = aug_load_file(cmd->aug, file); if (r < 0) ERR_REPORT(cmd, AUG_ECMDRUN, "Failed to load file %s", file); } static const struct command_opt_def cmd_load_file_opts[] = { { .type = CMD_PATH, .name = "file", .optional = false, .help = "the file to load" }, CMD_OPT_DEF_LAST }; static const char cmd_load_file_help[] = "Load a specific FILE, using autoload statements.\n"; static const struct command_def cmd_load_file_def = { .name = "load-file", .opts = cmd_load_file_opts, .handler = cmd_load_file, .synopsis = "load a specific file", .help = cmd_load_file_help }; static void cmd_save(struct command *cmd) { int r; r = aug_save(cmd->aug); if (r == -1) { ERR_REPORT(cmd, AUG_ECMDRUN, "saving failed (run 'errors' for details)"); } else { r = aug_match(cmd->aug, "/augeas/events/saved", NULL); if (r > 0) { fprintf(cmd->out, "Saved %d file(s)\n", r); } } } static const struct command_opt_def cmd_save_opts[] = { CMD_OPT_DEF_LAST }; static const struct command_def cmd_save_def = { .name = "save", .opts = cmd_save_opts, .handler = cmd_save, .synopsis = "save all pending changes", .help = "Save all pending changes to disk. How exactly that is done depends on\n the value of the node /augeas/save, which can be changed by the user.\n The possible values for it are\n \n noop - do not write files; useful for finding errors that\n might happen during a save\n backup - save the original file in a file by appending the extension\n '.augsave' and overwrite the original with new content\n newfile - leave the original file untouched and write new content to\n a file with extension '.augnew' next to the original file\n overwrite - overwrite the original file with new content\n \n Save always tries to save all files for which entries in the tree have\n changed. When saving fails, some files will be written. Details about\n why a save failed can by found by running the 'errors' command" }; static void cmd_load(struct command *cmd) { int r; r = aug_load(cmd->aug); if (r == -1) { ERR_REPORT(cmd, AUG_ECMDRUN, "loading failed (run 'errors' for details)"); } } static const struct command_opt_def cmd_load_opts[] = { CMD_OPT_DEF_LAST }; static const struct command_def cmd_load_def = { .name = "load", .opts = cmd_load_opts, .handler = cmd_load, .synopsis = "(re)load files under /files", .help = "Load files according to the transforms in /augeas/load. " "A transform\n Foo is represented with a subtree /augeas/load/Foo." " Underneath\n /augeas/load/Foo, one node labelled 'lens' must exist," " whose value is\n the fully qualified name of a lens, for example " "'Foo.lns', and\n multiple nodes 'incl' and 'excl' whose values are " "globs that determine\n which files are transformed by that lens. It " "is an error if one file\n can be processed by multiple transforms." }; static void cmd_info(struct command *cmd) { const char *v; int n; aug_get(cmd->aug, "/augeas/version", &v); ERR_RET(cmd); if (v != NULL) { fprintf(cmd->out, "version = %s\n", v); } aug_get(cmd->aug, "/augeas/root", &v); ERR_RET(cmd); if (v != NULL) { fprintf(cmd->out, "root = %s\n", v); } fprintf(cmd->out, "loadpath = "); for (char *entry = cmd->aug->modpathz; entry != NULL; entry = argz_next(cmd->aug->modpathz, cmd->aug->nmodpath, entry)) { if (entry != cmd->aug->modpathz) { fprintf(cmd->out, ":"); } fprintf(cmd->out, "%s", entry); } fprintf(cmd->out, "\n"); aug_get(cmd->aug, "/augeas/context", &v); ERR_RET(cmd); if (v == NULL) { v = "/"; } fprintf(cmd->out, "context = %s\n", v); n = aug_match(cmd->aug, "/augeas/files//path", NULL); fprintf(cmd->out, "num_files = %d\n", n); } static const struct command_opt_def cmd_info_opts[] = { CMD_OPT_DEF_LAST }; static const struct command_def cmd_info_def = { .name = "info", .opts = cmd_info_opts, .handler = cmd_info, .synopsis = "print runtime information", .help = "Print information about Augeas. The output contains:\n" " version : the version number from /augeas/version\n" " root : what Augeas considers the filesystem root\n" " from /augeas/root\n" " loadpath : the paths from which Augeas loads modules\n" " context : the context path (see context command)\n" " num_files : the number of files currently loaded (based on\n" " the number of /augeas/files//path nodes)" }; static void cmd_ins(struct command *cmd) { const char *label = arg_value(cmd, "label"); const char *where = arg_value(cmd, "where"); const char *path = arg_value(cmd, "path"); int before; if (STREQ(where, "after")) before = 0; else if (STREQ(where, "before")) before = 1; else { ERR_REPORT(cmd, AUG_ECMDRUN, "the argument for ins must be either 'before' or 'after'."); return; } aug_insert(cmd->aug, path, label, before); } static const struct command_opt_def cmd_ins_opts[] = { { .type = CMD_STR, .name = "label", .optional = false, .help = "the label for the new node" }, { .type = CMD_STR, .name = "where", .optional = false, .help = "either 'before' or 'after'" }, { .type = CMD_PATH, .name = "path", .optional = false, .help = "the node before/after which to insert" }, CMD_OPT_DEF_LAST }; static const char cmd_ins_help[] = "Insert a new node with label LABEL right before or after " "PATH into the\n tree. WHERE must be either 'before' or 'after'."; static const struct command_def cmd_ins_def = { .name = "ins", .opts = cmd_ins_opts, .handler = cmd_ins, .synopsis = "insert new node", .help = cmd_ins_help }; static const struct command_def cmd_insert_def = { .name = "insert", .opts = cmd_ins_opts, .handler = cmd_ins, .synopsis = "insert new node (alias of 'ins')", .help = cmd_ins_help }; static void cmd_store(struct command *cmd) { const char *lens = arg_value(cmd, "lens"); const char *path = arg_value(cmd, "path"); const char *node = arg_value(cmd, "node"); aug_text_store(cmd->aug, lens, node, path); } static const struct command_opt_def cmd_store_opts[] = { { .type = CMD_STR, .name = "lens", .optional = false, .help = "the name of the lens" }, { .type = CMD_PATH, .name = "node", .optional = false, .help = "where to find the input text" }, { .type = CMD_PATH, .name = "path", .optional = false, .help = "where to store parsed text" }, CMD_OPT_DEF_LAST }; static const char cmd_store_help[] = "Parse NODE using LENS and store the resulting tree at PATH."; static const struct command_def cmd_store_def = { .name = "store", .opts = cmd_store_opts, .handler = cmd_store, .synopsis = "parse text into tree", .help = cmd_store_help }; static void cmd_retrieve(struct command *cmd) { const char *lens = arg_value(cmd, "lens"); const char *node_in = arg_value(cmd, "node_in"); const char *path = arg_value(cmd, "path"); const char *node_out = arg_value(cmd, "node_out"); aug_text_retrieve(cmd->aug, lens, node_in, path, node_out); } static const struct command_opt_def cmd_retrieve_opts[] = { { .type = CMD_STR, .name = "lens", .optional = false, .help = "the name of the lens" }, { .type = CMD_PATH, .name = "node_in", .optional = false, .help = "the node containing the initial text (path expression)" }, { .type = CMD_PATH, .name = "path", .optional = false, .help = "the tree to transform (path expression)" }, { .type = CMD_PATH, .name = "node_out", .optional = false, .help = "where to store the resulting text (path expression)" }, CMD_OPT_DEF_LAST }; static const char cmd_retrieve_help[] = "Transform tree at PATH back into text using lens LENS and store the\n" " resulting string at NODE_OUT. Assume that the tree was initially read in\n" " with the same lens and the string stored at NODE_IN as input."; static const struct command_def cmd_retrieve_def = { .name = "retrieve", .opts = cmd_retrieve_opts, .handler = cmd_retrieve, .synopsis = "transform tree into text", .help = cmd_retrieve_help }; /* Given a path "/augeas/files/FILENAME/error", return FILENAME */ static char *err_filename(const char *match) { int noise = strlen(AUGEAS_META_FILES) + strlen("/error"); if (strlen(match) < noise + 1) goto error; return strndup(match + strlen(AUGEAS_META_FILES), strlen(match) - noise); error: return strdup("(no filename)"); } static const char *err_get(struct augeas *aug, const char *match, const char *child) { char *path = NULL; const char *value = ""; int r; r = pathjoin(&path, 2, match, child); ERR_NOMEM(r < 0, aug); aug_get(aug, path, &value); ERR_BAIL(aug); error: free(path); return value; } static void cmd_errors(struct command *cmd) { char **matches = NULL; int cnt = 0; struct augeas *aug = cmd->aug; cnt = aug_match(aug, "/augeas//error", &matches); ERR_BAIL(cmd); ERR_THROW(cnt < 0, aug, AUG_ECMDRUN, " (problem retrieving error messages)\n"); if (cnt == 0) { fprintf(cmd->out, " (no errors)\n"); goto done; } for (int i=0; i < cnt; i++) { const char *match = matches[i]; const char *line = err_get(aug, match, "line"); const char *char_pos = err_get(aug, match, "char"); const char *lens = err_get(aug, match, "lens"); const char *last = err_get(aug, match, "lens/last_matched"); const char *next = err_get(aug, match, "lens/next_not_matched"); const char *msg = err_get(aug, match, "message"); const char *path = err_get(aug, match, "path"); const char *kind = NULL; aug_get(aug, match, &kind); ERR_BAIL(aug); char *filename = err_filename(match); ERR_NOMEM(filename == NULL, aug); if (i>0) fprintf(cmd->out, "\n"); if (line != NULL) { fprintf(cmd->out, "Error in %s:%s.%s (%s)\n", filename, line, char_pos, kind); } else if (path != NULL) { fprintf(cmd->out, "Error in %s at node %s (%s)\n", filename, path, kind); } else { fprintf(cmd->out, "Error in %s (%s)\n", filename, kind); } FREE(filename); if (msg != NULL) fprintf(cmd->out, " %s\n", msg); if (lens != NULL) fprintf(cmd->out, " Lens: %s\n", lens); if (last != NULL) fprintf(cmd->out, " Last matched: %s\n", last); if (next != NULL) fprintf(cmd->out, " Next (no match): %s\n", next); } done: error: for (int i=0; i < cnt; i++) free(matches[i]); free(matches); } static const struct command_opt_def cmd_errors_opts[] = { CMD_OPT_DEF_LAST }; static const char cmd_errors_help[] = "Show all the errors encountered in processing files. For each error,\n" " print detailed information about where it happened and how. The same\n" " information can be retrieved by running 'print /augeas//error'\n\n" " For each error, the file in which the error occurred together with the\n" " line number and position in that line is shown, as well as information\n" " about the lens that encountered the error. For some errors, the last\n" " lens that matched successfully and the next lens that should have\n" " matched but didn't are also shown\n"; static const struct command_def cmd_errors_def = { .name = "errors", .opts = cmd_errors_opts, .handler = cmd_errors, .synopsis = "show all errors encountered in processing files", .help = cmd_errors_help }; /* Groups of commands */ static const struct command_grp_def cmd_grp_admin_def = { .name = "Admin", .commands = { &cmd_context_def, &cmd_load_def, &cmd_save_def, &cmd_transform_def, &cmd_load_file_def, &cmd_retrieve_def, &cmd_store_def, &cmd_quit_def, &cmd_def_last } }; static const struct command_grp_def cmd_grp_info_def = { .name = "Informational", .commands = { &cmd_errors_def, &cmd_info_def, &cmd_help_def, &cmd_source_def, &cmd_preview_def, &cmd_def_last } }; static const struct command_grp_def cmd_grp_read_def = { .name = "Read", .commands = { &cmd_dump_xml_def, &cmd_get_def, &cmd_label_def, &cmd_ls_def, &cmd_match_def, &cmd_count_def, &cmd_print_def, &cmd_span_def, &cmd_def_last } }; static const struct command_grp_def cmd_grp_write_def = { .name = "Write", .commands = { &cmd_clear_def, &cmd_clearm_def, &cmd_ins_def, &cmd_insert_def, &cmd_mv_def, &cmd_move_def, &cmd_cp_def, &cmd_copy_def, &cmd_rename_def, &cmd_rm_def, &cmd_set_def, &cmd_setm_def, &cmd_touch_def, &cmd_def_last } }; static const struct command_grp_def cmd_grp_pathx_def = { .name = "Path expression", .commands = { &cmd_defnode_def, &cmd_defvar_def, &cmd_def_last } }; static const struct command_grp_def *const cmd_groups[] = { &cmd_grp_admin_def, &cmd_grp_info_def, &cmd_grp_read_def, &cmd_grp_write_def, &cmd_grp_pathx_def, &cmd_grp_def_last }; static const struct command_def *lookup_cmd_def(const char *name) { for (int i = 0; cmd_groups[i]->name != NULL; i++) { for (int j = 0; cmd_groups[i]->commands[j]->name != NULL; j++) { if (STREQ(name, cmd_groups[i]->commands[j]->name)) return cmd_groups[i]->commands[j]; } } return NULL; } static void cmd_help(struct command *cmd) { const char *name = arg_value(cmd, "command"); char buf[100]; if (name == NULL) { //fprintf(cmd->out, "Commands:\n\n"); fprintf(cmd->out, "\n"); for (int i=0; cmd_groups[i]->name != NULL; i++) { fprintf(cmd->out, "%s commands:\n", cmd_groups[i]->name); for (int j=0; cmd_groups[i]->commands[j]->name != NULL; j++) { const struct command_def *def = cmd_groups[i]->commands[j]; fprintf(cmd->out, " %-10s - %s\n", def->name, def->synopsis); } fprintf(cmd->out, "\n"); } fprintf(cmd->out, "Type 'help ' for more information on a command\n\n"); } else { const struct command_def *def = lookup_cmd_def(name); const struct command_opt_def *odef = NULL; ERR_THROW(def == NULL, cmd->aug, AUG_ECMDRUN, "unknown command %s\n", name); fprintf(cmd->out, " COMMAND\n"); fprintf(cmd->out, " %s - %s\n\n", name, def->synopsis); fprintf(cmd->out, " SYNOPSIS\n"); fprintf(cmd->out, " %s", name); for (odef = def->opts; odef->name != NULL; odef++) { format_defname(buf, odef, true); fprintf(cmd->out, "%s", buf); } fprintf(cmd->out, "\n\n"); fprintf(cmd->out, " DESCRIPTION\n"); format_desc(def->help); if (def->opts->name != NULL) { fprintf(cmd->out, " OPTIONS\n"); for (odef = def->opts; odef->name != NULL; odef++) { const char *help = odef->help; if (help == NULL) help = ""; format_defname(buf, odef, false); fprintf(cmd->out, " %-10s %s\n", buf, help); } } fprintf(cmd->out, "\n"); } error: return; } int aug_srun(augeas *aug, FILE *out, const char *text) { char *line = NULL; const char *eol; struct command cmd; int result = 0; api_entry(aug); MEMZERO(&cmd, 1); cmd.aug = aug; cmd.error = aug->error; cmd.out = out; if (text == NULL) goto done; while (*text != '\0' && result >= 0) { eol = strchrnul(text, '\n'); while (isspace(*text) && text < eol) text++; if (*text == '\0') break; if (*text == '#' || text == eol) { text = (*eol == '\0') ? eol : eol + 1; continue; } line = strndup(text, eol - text); ERR_NOMEM(line == NULL, aug); if (parseline(&cmd, line) == 0) { cmd.def->handler(&cmd); result += 1; } else { result = -1; } ERR_BAIL(aug); if (result >= 0 && cmd.quit) { result = -2; goto done; } free_command_opts(&cmd); FREE(line); text = (*eol == '\0') ? eol : eol + 1; } done: free_command_opts(&cmd); FREE(line); api_exit(aug); return result; error: result = -1; goto done; } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/transform.c0000644000175000017500000012257714161102026012737 00000000000000/* * transform.c: support for building and running transformers * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include #include #include #include #include #include #include #include #include "internal.h" #include "memory.h" #include "augeas.h" #include "syntax.h" #include "transform.h" #include "errcode.h" static const int fnm_flags = FNM_PATHNAME; static const int glob_flags = GLOB_NOSORT; /* Extension for newly created files */ #define EXT_AUGNEW ".augnew" /* Extension for backup files */ #define EXT_AUGSAVE ".augsave" /* Loaded files are tracked underneath METATREE. When a file with name * FNAME is loaded, certain entries are made under METATREE / FNAME: * path : path where tree for FNAME is put * mtime : time of last modification of the file as reported by stat(2) * lens/info : information about where the applied lens was loaded from * lens/id : unique hexadecimal id of the lens * error : indication of errors during processing FNAME, or NULL * if processing succeeded * error/pos : position in file where error occurred (for get errors) * error/path: path to tree node where error occurred (for put errors) * error/message : human-readable error message */ static const char *const s_path = "path"; static const char *const s_lens = "lens"; static const char *const s_last = "last_matched"; static const char *const s_next = "next_not_matched"; static const char *const s_info = "info"; static const char *const s_mtime = "mtime"; static const char *const s_error = "error"; /* These are all put underneath "error" */ static const char *const s_pos = "pos"; static const char *const s_message = "message"; static const char *const s_line = "line"; static const char *const s_char = "char"; /* * Filters */ struct filter *make_filter(struct string *glb, unsigned int include) { struct filter *f; make_ref(f); f->glob = glb; f->include = include; return f; } void free_filter(struct filter *f) { if (f == NULL) return; assert(f->ref == 0); unref(f->next, filter); unref(f->glob, string); free(f); } static const char *pathbase(const char *path) { const char *p = strrchr(path, SEP); return (p == NULL) ? path : p + 1; } static bool is_excl(struct tree *f) { return streqv(f->label, "excl") && f->value != NULL; } static bool is_incl(struct tree *f) { return streqv(f->label, "incl") && f->value != NULL; } static bool is_regular_file(const char *path) { int r; struct stat st; r = stat(path, &st); if (r < 0) return false; return S_ISREG(st.st_mode); } static char *mtime_as_string(struct augeas *aug, const char *fname) { int r; struct stat st; char *result = NULL; if (fname == NULL) { result = strdup("0"); ERR_NOMEM(result == NULL, aug); goto done; } r = stat(fname, &st); if (r < 0) { /* If we fail to stat, silently ignore the error * and report an impossible mtime */ result = strdup("0"); ERR_NOMEM(result == NULL, aug); } else { r = xasprintf(&result, "%ld", (long) st.st_mtime); ERR_NOMEM(r < 0, aug); } done: return result; error: FREE(result); return NULL; } /* fnmatch(3) which will match // in a pattern to a path, like glob(3) does */ static int fnmatch_normalize(const char *pattern, const char *string, int flags) { int i, j, r; char *pattern_norm = NULL; r = ALLOC_N(pattern_norm, strlen(pattern) + 1); if (r < 0) goto error; for (i = 0, j = 0; i < strlen(pattern); i++) { if (pattern[i] != '/' || pattern[i+1] != '/') { pattern_norm[j] = pattern[i]; j++; } } pattern_norm[j] = 0; r = fnmatch(pattern_norm, string, flags); FREE(pattern_norm); return r; error: if (pattern_norm != NULL) FREE(pattern_norm); return -1; } static bool file_current(struct augeas *aug, const char *fname, struct tree *finfo) { struct tree *mtime = tree_child(finfo, s_mtime); struct tree *file = NULL, *path = NULL; int r; struct stat st; int64_t mtime_i; if (mtime == NULL || mtime->value == NULL) return false; r = xstrtoint64(mtime->value, 10, &mtime_i); if (r < 0) { /* Ignore silently and err on the side of caution */ return false; } r = stat(fname, &st); if (r < 0) return false; if (mtime_i != (int64_t) st.st_mtime) return false; path = tree_child(finfo, s_path); if (path == NULL) return false; file = tree_fpath(aug, path->value); return (file != NULL && ! file->dirty); } static int filter_generate(struct tree *xfm, const char *root, int *nmatches, char ***matches) { glob_t globbuf; int gl_flags = glob_flags; int r; int ret = 0; char **pathv = NULL; int pathc = 0; int root_prefix = strlen(root) - 1; *nmatches = 0; *matches = NULL; MEMZERO(&globbuf, 1); list_for_each(f, xfm->children) { char *globpat = NULL; if (! is_incl(f)) continue; pathjoin(&globpat, 2, root, f->value); r = glob(globpat, gl_flags, NULL, &globbuf); free(globpat); if (r != 0 && r != GLOB_NOMATCH) goto error; gl_flags |= GLOB_APPEND; } pathc = globbuf.gl_pathc; int pathind = 0; if (ALLOC_N(pathv, pathc) < 0) goto error; for (int i=0; i < pathc; i++) { const char *path = globbuf.gl_pathv[i] + root_prefix; bool include = true; list_for_each(e, xfm->children) { if (! is_excl(e)) continue; if (strchr(e->value, SEP) == NULL) path = pathbase(path); r = fnmatch_normalize(e->value, path, fnm_flags); if (r < 0) goto error; else if (r == 0) include = false; } if (include) include = is_regular_file(globbuf.gl_pathv[i]); if (include) { pathv[pathind] = strdup(globbuf.gl_pathv[i]); if (pathv[pathind] == NULL) goto error; pathind += 1; } } pathc = pathind; if (REALLOC_N(pathv, pathc) == -1) goto error; *matches = pathv; *nmatches = pathc; done: globfree(&globbuf); return ret; error: if (pathv != NULL) for (int i=0; i < pathc; i++) free(pathv[i]); free(pathv); ret = -1; goto done; } int filter_matches(struct tree *xfm, const char *path) { int found = 0; list_for_each(f, xfm->children) { if (is_incl(f) && fnmatch_normalize(f->value, path, fnm_flags) == 0) { found = 1; break; } } if (! found) return 0; list_for_each(f, xfm->children) { if (is_excl(f) && (fnmatch_normalize(f->value, path, fnm_flags) == 0)) return 0; } return 1; } /* * Transformers */ struct transform *make_transform(struct lens *lens, struct filter *filter) { struct transform *xform; make_ref(xform); xform->lens = lens; xform->filter = filter; return xform; } void free_transform(struct transform *xform) { if (xform == NULL) return; assert(xform->ref == 0); unref(xform->lens, lens); unref(xform->filter, filter); free(xform); } static char *err_path(const char *filename) { char *result = NULL; if (filename == NULL) pathjoin(&result, 2, AUGEAS_META_FILES, s_error); else pathjoin(&result, 3, AUGEAS_META_FILES, filename, s_error); return result; } ATTRIBUTE_FORMAT(printf, 4, 5) static struct tree *err_set(struct augeas *aug, struct tree *err_info, const char *sub, const char *format, ...) { int r; va_list ap; char *value = NULL; struct tree *tree = NULL; va_start(ap, format); r = vasprintf(&value, format, ap); va_end(ap); if (r < 0) value = NULL; ERR_NOMEM(r < 0, aug); tree = tree_child_cr(err_info, sub); ERR_NOMEM(tree == NULL, aug); r = tree_set_value(tree, value); ERR_NOMEM(r < 0, aug); error: free(value); return tree; } static struct tree *err_lens_entry(struct augeas *aug, struct tree *where, struct lens *lens, const char *label) { struct tree *result = NULL; if (lens == NULL) return NULL; char *fi = format_info(lens->info); if (fi != NULL) { result = err_set(aug, where, label, "%s", fi); free(fi); } return result; } /* Record an error in the tree. The error will show up underneath * /augeas/FILENAME/error if filename is not NULL, and underneath * /augeas/text/PATH otherwise. PATH is the path to the toplevel node in * the tree where the lens application happened. When STATUS is NULL, just * clear any error associated with FILENAME in the tree. */ static int store_error(struct augeas *aug, const char *filename, const char *path, const char *status, int errnum, const struct lns_error *err, const char *text) { struct tree *err_info = NULL, *finfo = NULL; char *fip = NULL; int r; int result = -1; if (filename != NULL) { r = pathjoin(&fip, 2, AUGEAS_META_FILES, filename); } else { r = pathjoin(&fip, 2, AUGEAS_META_TEXT, path); } ERR_NOMEM(r < 0, aug); finfo = tree_fpath_cr(aug, fip); ERR_BAIL(aug); if (status != NULL) { err_info = tree_child_cr(finfo, s_error); ERR_NOMEM(err_info == NULL, aug); r = tree_set_value(err_info, status); ERR_NOMEM(r < 0, aug); /* Errors from err_set are ignored on purpose. We try * to report as much as we can */ if (err != NULL) { if (err->pos >= 0) { size_t line, ofs; err_set(aug, err_info, s_pos, "%d", err->pos); if (text != NULL) { calc_line_ofs(text, err->pos, &line, &ofs); err_set(aug, err_info, s_line, "%zd", line); err_set(aug, err_info, s_char, "%zd", ofs); } } if (err->path != NULL) { err_set(aug, err_info, s_path, "%s%s", path, err->path); } struct tree *t = err_lens_entry(aug, err_info, err->lens, s_lens); if (t != NULL) { err_lens_entry(aug, t, err->last, s_last); err_lens_entry(aug, t, err->next, s_next); } err_set(aug, err_info, s_message, "%s", err->message); } else if (errnum != 0) { const char *msg = strerror(errnum); err_set(aug, err_info, s_message, "%s", msg); } } else { /* No error, nuke the error node if it exists */ err_info = tree_child(finfo, s_error); if (err_info != NULL) tree_unlink(aug, err_info); } tree_clean(finfo); result = 0; error: free(fip); return result; } /* Set up the file information in the /augeas tree. * * NODE must be the path to the file contents, and start with /files. * LENS is the lens used to transform the file. * Create entries under /augeas/NODE with some metadata about the file. * * Returns 0 on success, -1 on error */ static int add_file_info(struct augeas *aug, const char *node, struct lens *lens, const char *lens_name, const char *filename, bool force_reload) { struct tree *file, *tree; char *tmp = NULL; int r; char *path = NULL; int result = -1; if (lens == NULL) return -1; r = pathjoin(&path, 2, AUGEAS_META_TREE, node); ERR_NOMEM(r < 0, aug); file = tree_fpath_cr(aug, path); file->file = true; ERR_BAIL(aug); /* Set 'path' */ tree = tree_child_cr(file, s_path); ERR_NOMEM(tree == NULL, aug); r = tree_set_value(tree, node); ERR_NOMEM(r < 0, aug); /* Set 'mtime' */ if (force_reload) { tmp = strdup("0"); ERR_NOMEM(tmp == NULL, aug); } else { tmp = mtime_as_string(aug, filename); ERR_BAIL(aug); } tree = tree_child_cr(file, s_mtime); ERR_NOMEM(tree == NULL, aug); tree_store_value(tree, &tmp); /* Set 'lens/info' */ tmp = format_info(lens->info); ERR_NOMEM(tmp == NULL, aug); tree = tree_path_cr(file, 2, s_lens, s_info); ERR_NOMEM(tree == NULL, aug); r = tree_set_value(tree, tmp); ERR_NOMEM(r < 0, aug); FREE(tmp); /* Set 'lens' */ tree = tree->parent; r = tree_set_value(tree, lens_name); ERR_NOMEM(r < 0, aug); tree_clean(file); result = 0; error: free(path); free(tmp); return result; } static char *append_newline(char *text, size_t len) { /* Try to append a newline; this is a big hack to work */ /* around the fact that lenses generally break if the */ /* file does not end with a newline. */ if (len == 0 || text[len-1] != '\n') { if (REALLOC_N(text, len+2) == 0) { text[len] = '\n'; text[len+1] = '\0'; } } return text; } /* Turn the file name FNAME, which starts with aug->root, into * a path in the tree underneath /files */ static char *file_name_path(struct augeas *aug, const char *fname) { char *path = NULL; pathjoin(&path, 2, AUGEAS_FILES_TREE, fname + strlen(aug->root) - 1); return path; } /* Replace the subtree for FPATH with SUB */ static void tree_freplace(struct augeas *aug, const char *fpath, struct tree *sub) { struct tree *parent; parent = tree_fpath_cr(aug, fpath); ERR_RET(aug); parent->file = true; tree_unlink_children(aug, parent); list_append(parent->children, sub); list_for_each(s, sub) { s->parent = parent; } } static struct info* make_lns_info(struct augeas *aug, const char *filename, const char *text, int text_len) { struct info *info = NULL; make_ref(info); ERR_NOMEM(info == NULL, aug); if (filename != NULL) { make_ref(info->filename); ERR_NOMEM(info->filename == NULL, aug); info->filename->str = strdup(filename); } info->first_line = 1; info->last_line = 1; info->first_column = 1; if (text != NULL) { info->last_column = text_len; } info->error = aug->error; return info; error: unref(info, info); return NULL; } /* * Do the bookkeeping around calling lns_get that is common to load_file * and text_store, in particular, make sure the tree we read gets put into * the right place in AUG and that the span for that tree gets set. * * Transform TEXT using LENS and put the resulting tree at PATH. Use * FILENAME in error messages to indicate where the TEXT came from. */ static void lens_get(struct augeas *aug, struct lens *lens, const char *filename, const char *text, int text_len, const char *path, struct lns_error **err) { struct info *info = NULL; struct span *span = NULL; struct tree *tree = NULL; info = make_lns_info(aug, filename, text, text_len); ERR_BAIL(aug); if (aug->flags & AUG_ENABLE_SPAN) { /* Allocate the span already to capture a reference to info->filename */ span = make_span(info); ERR_NOMEM(span == NULL, info); } tree = lns_get(info, lens, text, aug->flags & AUG_ENABLE_SPAN, err); if (*err == NULL) { // Successful get tree_freplace(aug, path, tree); ERR_BAIL(aug); /* top level node span entire file length */ if (span != NULL && tree != NULL) { tree->parent->span = move(span); tree->parent->span->span_start = 0; tree->parent->span->span_end = text_len; } tree = NULL; } error: free_span(span); unref(info, info); free_tree(tree); } static int load_file(struct augeas *aug, struct lens *lens, const char *lens_name, char *filename) { char *text = NULL; const char *err_status = NULL; char *path = NULL; struct lns_error *err = NULL; int result = -1, r, text_len = 0; path = file_name_path(aug, filename); ERR_NOMEM(path == NULL, aug); r = add_file_info(aug, path, lens, lens_name, filename, false); if (r < 0) goto done; text = xread_file(filename); if (text == NULL) { err_status = "read_failed"; goto done; } text_len = strlen(text); text = append_newline(text, text_len); lens_get(aug, lens, filename, text, text_len, path, &err); if (err != NULL) { err_status = "parse_failed"; goto done; } ERR_BAIL(aug); result = 0; done: store_error(aug, filename + strlen(aug->root) - 1, path, err_status, errno, err, text); error: free_lns_error(err); free(path); free(text); return result; } /* The lens for a transform can be referred to in one of two ways: * either by a fully qualified name "Module.lens" or by the special * syntax "@Module"; the latter means we should take the lens from the * autoload transform for Module */ static struct lens *lens_from_name(struct augeas *aug, const char *name) { struct lens *result = NULL; if (name[0] == '@') { struct module *modl = NULL; for (modl = aug->modules; modl != NULL && !streqv(modl->name, name + 1); modl = modl->next); ERR_THROW(modl == NULL, aug, AUG_ENOLENS, "Could not find module %s", name + 1); ERR_THROW(modl->autoload == NULL, aug, AUG_ENOLENS, "No autoloaded lens in module %s", name + 1); result = modl->autoload->lens; } else { result = lens_lookup(aug, name); } ERR_THROW(result == NULL, aug, AUG_ENOLENS, "Can not find lens %s", name); return result; error: return NULL; } int text_store(struct augeas *aug, const char *lens_path, const char *path, const char *text) { struct lns_error *err = NULL; int result = -1; const char *err_status = NULL; struct lens *lens = NULL; lens = lens_from_name(aug, lens_path); ERR_BAIL(aug); lens_get(aug, lens, path, text, strlen(text), path, &err); if (err != NULL) { err_status = "parse_failed"; goto error; } ERR_BAIL(aug); result = 0; error: store_error(aug, NULL, path, err_status, errno, err, text); free_lns_error(err); return result; } const char *xfm_lens_name(struct tree *xfm) { struct tree *l = tree_child(xfm, s_lens); if (l == NULL) return "(unknown)"; if (l->value == NULL) return "(noname)"; return l->value; } struct lens *xfm_lens(struct augeas *aug, struct tree *xfm, const char **lens_name) { struct tree *l = NULL; if (lens_name != NULL) *lens_name = NULL; for (l = xfm->children; l != NULL && !streqv("lens", l->label); l = l->next); if (l == NULL || l->value == NULL) return NULL; if (lens_name != NULL) *lens_name = l->value; return lens_from_name(aug, l->value); } static void xfm_error(struct tree *xfm, const char *msg) { char *v = msg ? strdup(msg) : NULL; char *l = strdup("error"); if (l == NULL || v == NULL) { free(v); free(l); return; } tree_append(xfm, l, v); } int transform_validate(struct augeas *aug, struct tree *xfm) { struct tree *l = NULL; for (struct tree *t = xfm->children; t != NULL; ) { if (streqv(t->label, "lens")) { l = t; } else if ((is_incl(t) || (is_excl(t) && strchr(t->value, SEP) != NULL)) && t->value[0] != SEP) { /* Normalize relative paths to absolute ones */ int r; r = REALLOC_N(t->value, strlen(t->value) + 2); ERR_NOMEM(r < 0, aug); memmove(t->value + 1, t->value, strlen(t->value) + 1); t->value[0] = SEP; } if (streqv(t->label, "error")) { struct tree *del = t; t = del->next; tree_unlink(aug, del); } else { t = t->next; } } if (l == NULL) { xfm_error(xfm, "missing a child with label 'lens'"); return -1; } if (l->value == NULL) { xfm_error(xfm, "the 'lens' node does not contain a lens name"); return -1; } lens_from_name(aug, l->value); ERR_BAIL(aug); return 0; error: xfm_error(xfm, aug->error->details); /* We recorded this error in the tree, clear it so that future * operations report this exact same error (against the wrong lens) */ reset_error(aug->error); return -1; } void transform_file_error(struct augeas *aug, const char *status, const char *filename, const char *format, ...) { char *ep = err_path(filename); struct tree *err; char *msg; va_list ap; int r; err = tree_fpath_cr(aug, ep); FREE(ep); if (err == NULL) return; tree_unlink_children(aug, err); tree_set_value(err, status); err = tree_child_cr(err, s_message); if (err == NULL) return; va_start(ap, format); r = vasprintf(&msg, format, ap); va_end(ap); if (r < 0) return; tree_set_value(err, msg); free(msg); } static struct tree *file_info(struct augeas *aug, const char *fname) { char *path = NULL; struct tree *result = NULL; int r; r = pathjoin(&path, 2, AUGEAS_META_FILES, fname); ERR_NOMEM(r < 0, aug); result = tree_fpath(aug, path); ERR_BAIL(aug); error: free(path); return result; } int transform_load(struct augeas *aug, struct tree *xfm, const char *file) { int nmatches = 0; char **matches; const char *lens_name; struct lens *lens = xfm_lens(aug, xfm, &lens_name); int r; if (lens == NULL) { // FIXME: Record an error and return 0 return -1; } r = filter_generate(xfm, aug->root, &nmatches, &matches); if (r == -1) return -1; for (int i=0; i < nmatches; i++) { const char *filename = matches[i] + strlen(aug->root) - 1; struct tree *finfo = file_info(aug, filename); if (file != NULL && STRNEQ(filename, file)) { FREE(matches[i]); continue; } if (finfo != NULL && !finfo->dirty && tree_child(finfo, s_lens) != NULL) { /* We have a potential conflict: since FINFO is not marked as dirty (see aug_load for how the dirty flag on nodes under /augeas/files is used during loading), we already processed it with another lens. The other lens is recorded in FINFO. If it so happens that the lenses are actually the same, we silently move on, as this duplication does no harm. If they are different we definitely have a problem and need to record an error and remove the work the first lens did. */ const char *s = xfm_lens_name(finfo); struct lens *other_lens = lens_from_name(aug, s); if (lens != other_lens) { char *fpath = file_name_path(aug, matches[i]); transform_file_error(aug, "mxfm_load", filename, "Lenses %s and %s could be used to load this file", s, lens_name); aug_rm(aug, fpath); free(fpath); } } else if (!file_current(aug, matches[i], finfo)) { load_file(aug, lens, lens_name, matches[i]); } if (finfo != NULL) finfo->dirty = 0; FREE(matches[i]); } lens_release(lens); free(matches); return 0; } int transform_applies(struct tree *xfm, const char *path) { if (STRNEQLEN(path, AUGEAS_FILES_TREE, strlen(AUGEAS_FILES_TREE)) || path[strlen(AUGEAS_FILES_TREE)] != SEP) return 0; return filter_matches(xfm, path + strlen(AUGEAS_FILES_TREE)); } static int transfer_file_attrs(FILE *from, FILE *to, const char **err_status) { struct stat st; int ret = 0; int selinux_enabled = (is_selinux_enabled() > 0); security_context_t con = NULL; int from_fd; int to_fd = fileno(to); if (from == NULL) { *err_status = "replace_from_missing"; return -1; } from_fd = fileno(from); ret = fstat(from_fd, &st); if (ret < 0) { *err_status = "replace_stat"; return -1; } if (selinux_enabled) { if (fgetfilecon(from_fd, &con) < 0 && errno != ENOTSUP) { *err_status = "replace_getfilecon"; return -1; } } if (fchown(to_fd, st.st_uid, st.st_gid) < 0) { *err_status = "replace_chown"; return -1; } if (fchmod(to_fd, st.st_mode) < 0) { *err_status = "replace_chmod"; return -1; } if (selinux_enabled && con != NULL) { if (fsetfilecon(to_fd, con) < 0 && errno != ENOTSUP) { *err_status = "replace_setfilecon"; return -1; } freecon(con); } return 0; } /* Try to rename FROM to TO. If that fails with an error other than EXDEV * or EBUSY, return -1. If the failure is EXDEV or EBUSY (which we assume * means that FROM or TO is a bindmounted file), and COPY_IF_RENAME_FAILS * is true, copy the contents of FROM into TO and delete FROM. * * If COPY_IF_RENAME_FAILS and UNLINK_IF_RENAME_FAILS are true, and the above * copy mechanism is used, it will unlink the TO path and open with O_EXCL * to ensure we only copy *from* a bind mount rather than into an attacker's * mount placed at TO (e.g. for .augsave). * * Return 0 on success (either rename succeeded or we copied the contents * over successfully), -1 on failure. */ static int clone_file(const char *from, const char *to, const char **err_status, int copy_if_rename_fails, int unlink_if_rename_fails) { FILE *from_fp = NULL, *to_fp = NULL; char buf[BUFSIZ]; size_t len; int to_fd = -1, to_oflags, r; int result = -1; if (rename(from, to) == 0) return 0; if ((errno != EXDEV && errno != EBUSY) || !copy_if_rename_fails) { *err_status = "rename"; return -1; } /* rename not possible, copy file contents */ if (!(from_fp = fopen(from, "r"))) { *err_status = "clone_open_src"; goto done; } if (unlink_if_rename_fails) { r = unlink(to); if (r < 0) { *err_status = "clone_unlink_dst"; goto done; } } to_oflags = unlink_if_rename_fails ? O_EXCL : O_TRUNC; if ((to_fd = open(to, O_WRONLY|O_CREAT|to_oflags, S_IRUSR|S_IWUSR)) < 0) { *err_status = "clone_open_dst"; goto done; } if (!(to_fp = fdopen(to_fd, "w"))) { *err_status = "clone_fdopen_dst"; goto done; } if (transfer_file_attrs(from_fp, to_fp, err_status) < 0) goto done; while ((len = fread(buf, 1, BUFSIZ, from_fp)) > 0) { if (fwrite(buf, 1, len, to_fp) != len) { *err_status = "clone_write"; goto done; } } if (ferror(from_fp)) { *err_status = "clone_read"; goto done; } if (fflush(to_fp) != 0) { *err_status = "clone_flush"; goto done; } if (fsync(fileno(to_fp)) < 0) { *err_status = "clone_sync"; goto done; } result = 0; done: if (from_fp != NULL) fclose(from_fp); if (to_fp != NULL) { if (fclose(to_fp) != 0) { *err_status = "clone_fclose_dst"; result = -1; } } else if (to_fd >= 0 && close(to_fd) < 0) { *err_status = "clone_close_dst"; result = -1; } if (result != 0) unlink(to); if (result == 0) unlink(from); return result; } static char *strappend(const char *s1, const char *s2) { size_t len = strlen(s1) + strlen(s2); char *result = NULL, *p; if (ALLOC_N(result, len + 1) < 0) return NULL; p = stpcpy(result, s1); stpcpy(p, s2); return result; } static int file_saved_event(struct augeas *aug, const char *path) { const char *saved = strrchr(AUGEAS_EVENTS_SAVED, SEP) + 1; struct pathx *px; struct tree *dummy; int r; px = pathx_aug_parse(aug, aug->origin, NULL, AUGEAS_EVENTS_SAVED "[last()]", true); ERR_BAIL(aug); if (pathx_find_one(px, &dummy) == 1) { r = tree_insert(px, saved, 0); if (r < 0) goto error; } if (! tree_set(px, path)) goto error; free_pathx(px); return 0; error: free_pathx(px); return -1; } /* * Do the bookkeeping around calling LNS_PUT that's needed to update the * span after writing a tree to file */ static void lens_put(struct augeas *aug, const char *filename, struct lens *lens, const char *text, struct tree *tree, FILE *out, struct lns_error **err) { struct info *info = NULL; size_t text_len = strlen(text); bool with_span = aug->flags & AUG_ENABLE_SPAN; info = make_lns_info(aug, filename, text, text_len); ERR_BAIL(aug); if (with_span) { if (tree->span == NULL) { tree->span = make_span(info); ERR_NOMEM(tree->span == NULL, aug); } tree->span->span_start = ftell(out); } lns_put(info, out, lens, tree->children, text, aug->flags & AUG_ENABLE_SPAN, err); if (with_span) { tree->span->span_end = ftell(out); } error: unref(info, info); } /* * Save TREE->CHILDREN into the file PATH using the lens from XFORM. Errors * are noted in the /augeas/files hierarchy in AUG->ORIGIN under * PATH/error. * * Writing the file happens by first writing into a temp file, transferring all * file attributes of PATH to the temp file, and then renaming the temp file * back to PATH. * * Temp files are created alongside the destination file to enable the rename, * which may be the canonical path (PATH_canon) if PATH is a symlink. * * If the AUG_SAVE_NEWFILE flag is set, instead rename to PATH.augnew rather * than PATH. If AUG_SAVE_BACKUP is set, move the original to PATH.augsave. * (Always PATH.aug{new,save} irrespective of whether PATH is a symlink.) * * If the rename fails, and the entry AUGEAS_COPY_IF_FAILURE exists in * AUG->ORIGIN, PATH is instead overwritten by copying file contents. * * The table below shows the locations for each permutation. * * PATH save flag temp file dest file backup? * regular - PATH.XXXX PATH - * regular BACKUP PATH.XXXX PATH PATH.augsave * regular NEWFILE PATH.augnew.XXXX PATH.augnew - * symlink - PATH_canon.XXXX PATH_canon - * symlink BACKUP PATH_canon.XXXX PATH_canon PATH.augsave * symlink NEWFILE PATH.augnew.XXXX PATH.augnew - * * Return 0 on success, -1 on failure. */ int transform_save(struct augeas *aug, struct tree *xfm, const char *path, struct tree *tree) { int fd; FILE *fp = NULL, *augorig_canon_fp = NULL; char *augtemp = NULL, *augnew = NULL, *augorig = NULL, *augsave = NULL; char *augorig_canon = NULL, *augdest = NULL; int augorig_exists; int copy_if_rename_fails = 0; char *text = NULL; const char *filename = path + strlen(AUGEAS_FILES_TREE) + 1; const char *err_status = NULL; char *dyn_err_status = NULL; struct lns_error *err = NULL; const char *lens_name; struct lens *lens = xfm_lens(aug, xfm, &lens_name); int result = -1, r; bool force_reload; struct info *info = NULL; errno = 0; if (lens == NULL) { err_status = "lens_name"; goto done; } copy_if_rename_fails = aug_get(aug, AUGEAS_COPY_IF_RENAME_FAILS, NULL) == 1; if (asprintf(&augorig, "%s%s", aug->root, filename) == -1) { augorig = NULL; goto done; } augorig_canon = canonicalize_file_name(augorig); augorig_exists = 1; if (augorig_canon == NULL) { if (errno == ENOENT) { augorig_canon = augorig; augorig_exists = 0; } else { err_status = "canon_augorig"; goto done; } } if (access(augorig_canon, R_OK) == 0) { augorig_canon_fp = fopen(augorig_canon, "r"); text = xfread_file(augorig_canon_fp); } else { text = strdup(""); } if (text == NULL) { err_status = "put_read"; goto done; } text = append_newline(text, strlen(text)); /* Figure out where to put the .augnew and temp file. If no .augnew file then put the temp file next to augorig_canon, else next to .augnew. */ if (aug->flags & AUG_SAVE_NEWFILE) { if (xasprintf(&augnew, "%s" EXT_AUGNEW, augorig) < 0) { err_status = "augnew_oom"; goto done; } augdest = augnew; } else { augdest = augorig_canon; } if (xasprintf(&augtemp, "%s.XXXXXX", augdest) < 0) { err_status = "augtemp_oom"; goto done; } // FIXME: We might have to create intermediate directories // to be able to write augnew, but we have no idea what permissions // etc. they should get. Just the process default ? fd = mkstemp(augtemp); if (fd < 0) { err_status = "mk_augtemp"; goto done; } fp = fdopen(fd, "w"); if (fp == NULL) { err_status = "open_augtemp"; goto done; } if (augorig_exists) { if (transfer_file_attrs(augorig_canon_fp, fp, &err_status) != 0) { goto done; } } else { /* Since mkstemp is used, the temp file will have secure permissions * instead of those implied by umask, so change them for new files */ mode_t curumsk = umask(022); umask(curumsk); if (fchmod(fileno(fp), 0666 & ~curumsk) < 0) { err_status = "create_chmod"; goto done; } } if (tree != NULL) { lens_put(aug, augorig_canon, lens, text, tree, fp, &err); ERR_BAIL(aug); } if (ferror(fp)) { err_status = "error_augtemp"; goto done; } if (fflush(fp) != 0) { err_status = "flush_augtemp"; goto done; } if (fsync(fileno(fp)) < 0) { err_status = "sync_augtemp"; goto done; } if (fclose(fp) != 0) { err_status = "close_augtemp"; fp = NULL; goto done; } fp = NULL; if (err != NULL) { err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed"; unlink(augtemp); goto done; } { char *new_text = xread_file(augtemp); int same = 0; if (new_text == NULL) { err_status = "read_augtemp"; goto done; } same = STREQ(text, new_text); FREE(new_text); if (same) { result = 0; unlink(augtemp); goto done; } else if (aug->flags & AUG_SAVE_NOOP) { result = 1; unlink(augtemp); goto done; } } if (!(aug->flags & AUG_SAVE_NEWFILE)) { if (augorig_exists && (aug->flags & AUG_SAVE_BACKUP)) { r = xasprintf(&augsave, "%s" EXT_AUGSAVE, augorig); if (r == -1) { augsave = NULL; goto done; } r = clone_file(augorig_canon, augsave, &err_status, 1, 1); if (r != 0) { dyn_err_status = strappend(err_status, "_augsave"); goto done; } } } r = clone_file(augtemp, augdest, &err_status, copy_if_rename_fails, 0); if (r != 0) { unlink(augtemp); dyn_err_status = strappend(err_status, "_augtemp"); goto done; } result = 1; done: force_reload = aug->flags & AUG_SAVE_NEWFILE; r = add_file_info(aug, path, lens, lens_name, augorig, force_reload); if (r < 0) { err_status = "file_info"; result = -1; } if (result > 0) { r = file_saved_event(aug, path); if (r < 0) { err_status = "saved_event"; result = -1; } } { const char *emsg = dyn_err_status == NULL ? err_status : dyn_err_status; store_error(aug, filename, path, emsg, errno, err, text); } error: free(dyn_err_status); lens_release(lens); free(text); free(augtemp); free(augnew); if (augorig_canon != augorig) free(augorig_canon); free(augorig); free(augsave); free_lns_error(err); unref(info, info); if (fp != NULL) fclose(fp); if (augorig_canon_fp != NULL) fclose(augorig_canon_fp); return result; } int text_retrieve(struct augeas *aug, const char *lens_name, const char *path, struct tree *tree, const char *text_in, char **text_out) { struct memstream ms; bool ms_open = false; const char *err_status = NULL; struct lns_error *err = NULL; struct lens *lens = NULL; int result = -1, r; struct info *info = NULL; MEMZERO(&ms, 1); errno = 0; lens = lens_from_name(aug, lens_name); if (lens == NULL) { err_status = "lens_name"; goto done; } r = init_memstream(&ms); if (r < 0) { err_status = "init_memstream"; goto done; } ms_open = true; if (tree != NULL) { lens_put(aug, path, lens, text_in, tree, ms.stream, &err); ERR_BAIL(aug); } r = close_memstream(&ms); ms_open = false; if (r < 0) { err_status = "close_memstream"; goto done; } *text_out = ms.buf; ms.buf = NULL; if (err != NULL) { err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed"; goto done; } result = 0; done: store_error(aug, NULL, path, err_status, errno, err, text_in); error: lens_release(lens); if (result < 0) { free(*text_out); *text_out = NULL; } free_lns_error(err); unref(info, info); if (ms_open) close_memstream(&ms); return result; } int remove_file(struct augeas *aug, struct tree *tree) { const char *err_status = NULL; char *dyn_err_status = NULL; char *augsave = NULL, *augorig = NULL, *augorig_canon = NULL; struct tree *path = NULL; const char *file_path = NULL; char *meta_path = NULL; int r; path = tree_child(tree, s_path); if (path == NULL) { err_status = "no child called 'path' for file entry"; goto error; } file_path = path->value + strlen(AUGEAS_FILES_TREE); path = NULL; meta_path = path_of_tree(tree); if (meta_path == NULL) { err_status = "path_of_tree"; goto error; } if ((augorig = strappend(aug->root, file_path)) == NULL) { err_status = "root_file"; goto error; } augorig_canon = canonicalize_file_name(augorig); if (augorig_canon == NULL) { if (errno == ENOENT) { goto done; } else { err_status = "canon_augorig"; goto error; } } r = file_saved_event(aug, meta_path + strlen(AUGEAS_META_TREE)); if (r < 0) { err_status = "saved_event"; goto error; } if (aug->flags & AUG_SAVE_NOOP) goto done; if (aug->flags & AUG_SAVE_BACKUP) { /* Move file to one with extension .augsave */ r = asprintf(&augsave, "%s" EXT_AUGSAVE, augorig_canon); if (r == -1) { augsave = NULL; goto error; } r = clone_file(augorig_canon, augsave, &err_status, 1, 1); if (r != 0) { dyn_err_status = strappend(err_status, "_augsave"); goto error; } } else { /* Unlink file */ r = unlink(augorig_canon); if (r < 0) { err_status = "unlink_orig"; goto error; } } path = NULL; tree_unlink(aug, tree); done: free(meta_path); free(augorig); free(augorig_canon); free(augsave); return 0; error: { const char *emsg = dyn_err_status == NULL ? err_status : dyn_err_status; store_error(aug, file_path, meta_path, emsg, errno, NULL, NULL); } free(meta_path); free(augorig); free(augorig_canon); free(augsave); free(dyn_err_status); return -1; } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/Makefile.in0000644000175000017500000025352414161102661012627 00000000000000# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # -*- Makefile-automake -*- # # Support for running programs with failmalloc preloaded. Include in other # automake files and make sure the following variables are set: # # FAILMALLOC_START - number of first FAILMALLOC_INTERVAL # FAILMALLOC_REP - how often to repeat with increasing FAILMALLOC_INTERVAL # FAILMALLOC_PROG - the program to run with linfailmalloc preloaded VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = augtool$(EXEEXT) augparse$(EXEEXT) augmatch$(EXEEXT) subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/gnulib/m4/00gnulib.m4 \ $(top_srcdir)/gnulib/m4/__inline.m4 \ $(top_srcdir)/gnulib/m4/absolute-header.m4 \ $(top_srcdir)/gnulib/m4/alloca.m4 \ $(top_srcdir)/gnulib/m4/argz.m4 \ $(top_srcdir)/gnulib/m4/arpa_inet_h.m4 \ $(top_srcdir)/gnulib/m4/btowc.m4 \ $(top_srcdir)/gnulib/m4/builtin-expect.m4 \ $(top_srcdir)/gnulib/m4/canonicalize.m4 \ $(top_srcdir)/gnulib/m4/clock_time.m4 \ $(top_srcdir)/gnulib/m4/close.m4 \ $(top_srcdir)/gnulib/m4/codeset.m4 \ $(top_srcdir)/gnulib/m4/ctype.m4 \ $(top_srcdir)/gnulib/m4/double-slash-root.m4 \ $(top_srcdir)/gnulib/m4/dup2.m4 \ $(top_srcdir)/gnulib/m4/eealloc.m4 \ $(top_srcdir)/gnulib/m4/environ.m4 \ $(top_srcdir)/gnulib/m4/errno_h.m4 \ $(top_srcdir)/gnulib/m4/exponentd.m4 \ $(top_srcdir)/gnulib/m4/extensions.m4 \ $(top_srcdir)/gnulib/m4/extern-inline.m4 \ $(top_srcdir)/gnulib/m4/fcntl-o.m4 \ $(top_srcdir)/gnulib/m4/fcntl.m4 \ $(top_srcdir)/gnulib/m4/fcntl_h.m4 \ $(top_srcdir)/gnulib/m4/fdopen.m4 \ $(top_srcdir)/gnulib/m4/flexmember.m4 \ $(top_srcdir)/gnulib/m4/float_h.m4 \ $(top_srcdir)/gnulib/m4/fnmatch.m4 \ $(top_srcdir)/gnulib/m4/fnmatch_h.m4 \ $(top_srcdir)/gnulib/m4/fpieee.m4 \ $(top_srcdir)/gnulib/m4/free.m4 \ $(top_srcdir)/gnulib/m4/fstat.m4 \ $(top_srcdir)/gnulib/m4/ftruncate.m4 \ $(top_srcdir)/gnulib/m4/getcwd.m4 \ $(top_srcdir)/gnulib/m4/getdelim.m4 \ $(top_srcdir)/gnulib/m4/getdtablesize.m4 \ $(top_srcdir)/gnulib/m4/getline.m4 \ $(top_srcdir)/gnulib/m4/getopt.m4 \ $(top_srcdir)/gnulib/m4/getpagesize.m4 \ $(top_srcdir)/gnulib/m4/getrandom.m4 \ $(top_srcdir)/gnulib/m4/gettimeofday.m4 \ $(top_srcdir)/gnulib/m4/gnulib-common.m4 \ $(top_srcdir)/gnulib/m4/gnulib-comp.m4 \ $(top_srcdir)/gnulib/m4/include_next.m4 \ $(top_srcdir)/gnulib/m4/inet_pton.m4 \ $(top_srcdir)/gnulib/m4/intl-thread-locale.m4 \ $(top_srcdir)/gnulib/m4/intlmacosx.m4 \ $(top_srcdir)/gnulib/m4/intmax_t.m4 \ $(top_srcdir)/gnulib/m4/inttypes.m4 \ $(top_srcdir)/gnulib/m4/inttypes_h.m4 \ $(top_srcdir)/gnulib/m4/ioctl.m4 \ $(top_srcdir)/gnulib/m4/isblank.m4 \ $(top_srcdir)/gnulib/m4/langinfo_h.m4 \ $(top_srcdir)/gnulib/m4/largefile.m4 \ $(top_srcdir)/gnulib/m4/lcmessage.m4 \ $(top_srcdir)/gnulib/m4/libtool.m4 \ $(top_srcdir)/gnulib/m4/limits-h.m4 \ $(top_srcdir)/gnulib/m4/localcharset.m4 \ $(top_srcdir)/gnulib/m4/locale-fr.m4 \ $(top_srcdir)/gnulib/m4/locale-ja.m4 \ $(top_srcdir)/gnulib/m4/locale-tr.m4 \ $(top_srcdir)/gnulib/m4/locale-zh.m4 \ $(top_srcdir)/gnulib/m4/locale_h.m4 \ $(top_srcdir)/gnulib/m4/localeconv.m4 \ $(top_srcdir)/gnulib/m4/localename.m4 \ $(top_srcdir)/gnulib/m4/lock.m4 \ $(top_srcdir)/gnulib/m4/lstat.m4 \ $(top_srcdir)/gnulib/m4/ltoptions.m4 \ $(top_srcdir)/gnulib/m4/ltsugar.m4 \ $(top_srcdir)/gnulib/m4/ltversion.m4 \ $(top_srcdir)/gnulib/m4/lt~obsolete.m4 \ $(top_srcdir)/gnulib/m4/malloc.m4 \ $(top_srcdir)/gnulib/m4/malloca.m4 \ $(top_srcdir)/gnulib/m4/mbrtowc.m4 \ $(top_srcdir)/gnulib/m4/mbsinit.m4 \ $(top_srcdir)/gnulib/m4/mbsrtowcs.m4 \ $(top_srcdir)/gnulib/m4/mbstate_t.m4 \ $(top_srcdir)/gnulib/m4/mbtowc.m4 \ $(top_srcdir)/gnulib/m4/memchr.m4 \ $(top_srcdir)/gnulib/m4/mempcpy.m4 \ $(top_srcdir)/gnulib/m4/minmax.m4 \ $(top_srcdir)/gnulib/m4/mkdir.m4 \ $(top_srcdir)/gnulib/m4/mkstemp.m4 \ $(top_srcdir)/gnulib/m4/mmap-anon.m4 \ $(top_srcdir)/gnulib/m4/mode_t.m4 \ $(top_srcdir)/gnulib/m4/msvc-inval.m4 \ $(top_srcdir)/gnulib/m4/msvc-nothrow.m4 \ $(top_srcdir)/gnulib/m4/multiarch.m4 \ $(top_srcdir)/gnulib/m4/musl.m4 \ $(top_srcdir)/gnulib/m4/nanosleep.m4 \ $(top_srcdir)/gnulib/m4/netinet_in_h.m4 \ $(top_srcdir)/gnulib/m4/nl_langinfo.m4 \ $(top_srcdir)/gnulib/m4/nocrash.m4 \ $(top_srcdir)/gnulib/m4/off_t.m4 \ $(top_srcdir)/gnulib/m4/open-cloexec.m4 \ $(top_srcdir)/gnulib/m4/open-slash.m4 \ $(top_srcdir)/gnulib/m4/open.m4 \ $(top_srcdir)/gnulib/m4/pathmax.m4 \ $(top_srcdir)/gnulib/m4/perror.m4 \ $(top_srcdir)/gnulib/m4/pipe.m4 \ $(top_srcdir)/gnulib/m4/printf.m4 \ $(top_srcdir)/gnulib/m4/pthread-thread.m4 \ $(top_srcdir)/gnulib/m4/pthread_h.m4 \ $(top_srcdir)/gnulib/m4/pthread_rwlock_rdlock.m4 \ $(top_srcdir)/gnulib/m4/pthread_sigmask.m4 \ $(top_srcdir)/gnulib/m4/putenv.m4 \ $(top_srcdir)/gnulib/m4/raise.m4 \ $(top_srcdir)/gnulib/m4/rawmemchr.m4 \ $(top_srcdir)/gnulib/m4/readlink.m4 \ $(top_srcdir)/gnulib/m4/regex.m4 \ $(top_srcdir)/gnulib/m4/safe-alloc.m4 \ $(top_srcdir)/gnulib/m4/sched_h.m4 \ $(top_srcdir)/gnulib/m4/sched_yield.m4 \ $(top_srcdir)/gnulib/m4/select.m4 \ $(top_srcdir)/gnulib/m4/selinux-context-h.m4 \ $(top_srcdir)/gnulib/m4/selinux-label-h.m4 \ $(top_srcdir)/gnulib/m4/selinux-selinux-h.m4 \ $(top_srcdir)/gnulib/m4/semaphore.m4 \ $(top_srcdir)/gnulib/m4/setenv.m4 \ $(top_srcdir)/gnulib/m4/setlocale.m4 \ $(top_srcdir)/gnulib/m4/setlocale_null.m4 \ $(top_srcdir)/gnulib/m4/sigaction.m4 \ $(top_srcdir)/gnulib/m4/signal_h.m4 \ $(top_srcdir)/gnulib/m4/signalblocking.m4 \ $(top_srcdir)/gnulib/m4/size_max.m4 \ $(top_srcdir)/gnulib/m4/sleep.m4 \ $(top_srcdir)/gnulib/m4/socketlib.m4 \ $(top_srcdir)/gnulib/m4/sockets.m4 \ $(top_srcdir)/gnulib/m4/socklen.m4 \ $(top_srcdir)/gnulib/m4/sockpfaf.m4 \ $(top_srcdir)/gnulib/m4/ssize_t.m4 \ $(top_srcdir)/gnulib/m4/stat-time.m4 \ $(top_srcdir)/gnulib/m4/stat.m4 \ $(top_srcdir)/gnulib/m4/stdalign.m4 \ $(top_srcdir)/gnulib/m4/stdbool.m4 \ $(top_srcdir)/gnulib/m4/stddef_h.m4 \ $(top_srcdir)/gnulib/m4/stdint.m4 \ $(top_srcdir)/gnulib/m4/stdint_h.m4 \ $(top_srcdir)/gnulib/m4/stdio_h.m4 \ $(top_srcdir)/gnulib/m4/stdlib_h.m4 \ $(top_srcdir)/gnulib/m4/stpcpy.m4 \ $(top_srcdir)/gnulib/m4/stpncpy.m4 \ $(top_srcdir)/gnulib/m4/strchrnul.m4 \ $(top_srcdir)/gnulib/m4/strerror.m4 \ $(top_srcdir)/gnulib/m4/strerror_r.m4 \ $(top_srcdir)/gnulib/m4/string_h.m4 \ $(top_srcdir)/gnulib/m4/strndup.m4 \ $(top_srcdir)/gnulib/m4/strnlen.m4 \ $(top_srcdir)/gnulib/m4/strstr.m4 \ $(top_srcdir)/gnulib/m4/symlink.m4 \ $(top_srcdir)/gnulib/m4/sys_ioctl_h.m4 \ $(top_srcdir)/gnulib/m4/sys_random_h.m4 \ $(top_srcdir)/gnulib/m4/sys_select_h.m4 \ $(top_srcdir)/gnulib/m4/sys_socket_h.m4 \ $(top_srcdir)/gnulib/m4/sys_stat_h.m4 \ $(top_srcdir)/gnulib/m4/sys_time_h.m4 \ $(top_srcdir)/gnulib/m4/sys_types_h.m4 \ $(top_srcdir)/gnulib/m4/sys_uio_h.m4 \ $(top_srcdir)/gnulib/m4/sys_wait_h.m4 \ $(top_srcdir)/gnulib/m4/tempname.m4 \ $(top_srcdir)/gnulib/m4/thread.m4 \ $(top_srcdir)/gnulib/m4/threadlib.m4 \ $(top_srcdir)/gnulib/m4/time_h.m4 \ $(top_srcdir)/gnulib/m4/unistd_h.m4 \ $(top_srcdir)/gnulib/m4/usleep.m4 \ $(top_srcdir)/gnulib/m4/vasnprintf.m4 \ $(top_srcdir)/gnulib/m4/vasprintf.m4 \ $(top_srcdir)/gnulib/m4/visibility.m4 \ $(top_srcdir)/gnulib/m4/warn-on-use.m4 \ $(top_srcdir)/gnulib/m4/wchar_h.m4 \ $(top_srcdir)/gnulib/m4/wchar_t.m4 \ $(top_srcdir)/gnulib/m4/wcrtomb.m4 \ $(top_srcdir)/gnulib/m4/wctob.m4 \ $(top_srcdir)/gnulib/m4/wctomb.m4 \ $(top_srcdir)/gnulib/m4/wctype_h.m4 \ $(top_srcdir)/gnulib/m4/wint_t.m4 \ $(top_srcdir)/gnulib/m4/wmemchr.m4 \ $(top_srcdir)/gnulib/m4/wmempcpy.m4 \ $(top_srcdir)/gnulib/m4/xsize.m4 \ $(top_srcdir)/gnulib/m4/yield.m4 \ $(top_srcdir)/gnulib/m4/zzgnulib.m4 $(top_srcdir)/acinclude.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" \ "$(DESTDIR)$(includedir)" PROGRAMS = $(bin_PROGRAMS) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) am__DEPENDENCIES_1 = libaugeas_la_DEPENDENCIES = liblexer.la libfa.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(GNULIB) am_libaugeas_la_OBJECTS = augeas.lo augrun.lo pathx.lo internal.lo \ memory.lo ref.lo syntax.lo parser.lo builtin.lo lens.lo \ regexp.lo transform.lo ast.lo get.lo put.lo info.lo errcode.lo \ jmt.lo xml.lo libaugeas_la_OBJECTS = $(am_libaugeas_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libaugeas_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(libaugeas_la_LDFLAGS) $(LDFLAGS) -o $@ libfa_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(GNULIB) am_libfa_la_OBJECTS = fa.lo hash.lo memory.lo ref.lo libfa_la_OBJECTS = $(am_libfa_la_OBJECTS) libfa_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(libfa_la_LDFLAGS) $(LDFLAGS) -o $@ liblexer_la_LIBADD = am_liblexer_la_OBJECTS = liblexer_la-lexer.lo liblexer_la_OBJECTS = $(am_liblexer_la_OBJECTS) liblexer_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(liblexer_la_CFLAGS) \ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ am_augmatch_OBJECTS = augmatch.$(OBJEXT) augmatch_OBJECTS = $(am_augmatch_OBJECTS) augmatch_DEPENDENCIES = libaugeas.la $(am__DEPENDENCIES_1) $(GNULIB) am_augparse_OBJECTS = augparse.$(OBJEXT) augparse_OBJECTS = $(am_augparse_OBJECTS) augparse_DEPENDENCIES = libaugeas.la $(am__DEPENDENCIES_1) $(GNULIB) am_augtool_OBJECTS = augtool.$(OBJEXT) augtool_OBJECTS = $(am_augtool_OBJECTS) augtool_DEPENDENCIES = libaugeas.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(GNULIB) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/build/ac-aux/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/ast.Plo ./$(DEPDIR)/augeas.Plo \ ./$(DEPDIR)/augmatch.Po ./$(DEPDIR)/augparse.Po \ ./$(DEPDIR)/augrun.Plo ./$(DEPDIR)/augtool.Po \ ./$(DEPDIR)/builtin.Plo ./$(DEPDIR)/errcode.Plo \ ./$(DEPDIR)/fa.Plo ./$(DEPDIR)/get.Plo ./$(DEPDIR)/hash.Plo \ ./$(DEPDIR)/info.Plo ./$(DEPDIR)/internal.Plo \ ./$(DEPDIR)/jmt.Plo ./$(DEPDIR)/lens.Plo \ ./$(DEPDIR)/liblexer_la-lexer.Plo ./$(DEPDIR)/memory.Plo \ ./$(DEPDIR)/parser.Plo ./$(DEPDIR)/pathx.Plo \ ./$(DEPDIR)/put.Plo ./$(DEPDIR)/ref.Plo ./$(DEPDIR)/regexp.Plo \ ./$(DEPDIR)/syntax.Plo ./$(DEPDIR)/transform.Plo \ ./$(DEPDIR)/xml.Plo am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS) LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS) AM_V_LEX = $(am__v_LEX_@AM_V@) am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@) am__v_LEX_0 = @echo " LEX " $@; am__v_LEX_1 = YLWRAP = $(top_srcdir)/build/ac-aux/ylwrap am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \ -e s/c++$$/h++/ -e s/c$$/h/ YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS) LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS) AM_V_YACC = $(am__v_YACC_@AM_V@) am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@) am__v_YACC_0 = @echo " YACC " $@; am__v_YACC_1 = SOURCES = $(libaugeas_la_SOURCES) $(libfa_la_SOURCES) \ $(liblexer_la_SOURCES) $(augmatch_SOURCES) $(augparse_SOURCES) \ $(augtool_SOURCES) DIST_SOURCES = $(libaugeas_la_SOURCES) $(libfa_la_SOURCES) \ $(liblexer_la_SOURCES) $(augmatch_SOURCES) $(augparse_SOURCES) \ $(augtool_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(include_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.inc \ $(top_srcdir)/build/ac-aux/depcomp \ $(top_srcdir)/build/ac-aux/ylwrap lexer.c parser.c parser.h DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALLOCA = @ALLOCA@ ALLOCA_H = @ALLOCA_H@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@ AR = @AR@ ARFLAGS = @ARFLAGS@ ARGZ_H = @ARGZ_H@ AUGEAS_CFLAGS = @AUGEAS_CFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@ BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@ BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@ BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@ BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CFLAG_VISIBILITY = @CFLAG_VISIBILITY@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EMULTIHOP_HIDDEN = @EMULTIHOP_HIDDEN@ EMULTIHOP_VALUE = @EMULTIHOP_VALUE@ ENOLINK_HIDDEN = @ENOLINK_HIDDEN@ ENOLINK_VALUE = @ENOLINK_VALUE@ EOVERFLOW_HIDDEN = @EOVERFLOW_HIDDEN@ EOVERFLOW_VALUE = @EOVERFLOW_VALUE@ ERRNO_H = @ERRNO_H@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FLOAT_H = @FLOAT_H@ FNMATCH_H = @FNMATCH_H@ GETOPT_CDEFS_H = @GETOPT_CDEFS_H@ GETOPT_H = @GETOPT_H@ GNULIB_ACCEPT = @GNULIB_ACCEPT@ GNULIB_ACCEPT4 = @GNULIB_ACCEPT4@ GNULIB_ACCESS = @GNULIB_ACCESS@ GNULIB_ALIGNED_ALLOC = @GNULIB_ALIGNED_ALLOC@ GNULIB_ATOLL = @GNULIB_ATOLL@ GNULIB_BIND = @GNULIB_BIND@ GNULIB_BTOWC = @GNULIB_BTOWC@ GNULIB_CALLOC_POSIX = @GNULIB_CALLOC_POSIX@ GNULIB_CANONICALIZE_FILE_NAME = @GNULIB_CANONICALIZE_FILE_NAME@ GNULIB_CHDIR = @GNULIB_CHDIR@ GNULIB_CHOWN = @GNULIB_CHOWN@ GNULIB_CLOSE = @GNULIB_CLOSE@ GNULIB_CONNECT = @GNULIB_CONNECT@ GNULIB_COPY_FILE_RANGE = @GNULIB_COPY_FILE_RANGE@ GNULIB_CREAT = @GNULIB_CREAT@ GNULIB_CTIME = @GNULIB_CTIME@ GNULIB_DPRINTF = @GNULIB_DPRINTF@ GNULIB_DUP = @GNULIB_DUP@ GNULIB_DUP2 = @GNULIB_DUP2@ GNULIB_DUP3 = @GNULIB_DUP3@ GNULIB_DUPLOCALE = @GNULIB_DUPLOCALE@ GNULIB_ENVIRON = @GNULIB_ENVIRON@ GNULIB_EUIDACCESS = @GNULIB_EUIDACCESS@ GNULIB_EXECL = @GNULIB_EXECL@ GNULIB_EXECLE = @GNULIB_EXECLE@ GNULIB_EXECLP = @GNULIB_EXECLP@ GNULIB_EXECV = @GNULIB_EXECV@ GNULIB_EXECVE = @GNULIB_EXECVE@ GNULIB_EXECVP = @GNULIB_EXECVP@ GNULIB_EXECVPE = @GNULIB_EXECVPE@ GNULIB_EXPLICIT_BZERO = @GNULIB_EXPLICIT_BZERO@ GNULIB_FACCESSAT = @GNULIB_FACCESSAT@ GNULIB_FCHDIR = @GNULIB_FCHDIR@ GNULIB_FCHMODAT = @GNULIB_FCHMODAT@ GNULIB_FCHOWNAT = @GNULIB_FCHOWNAT@ GNULIB_FCLOSE = @GNULIB_FCLOSE@ GNULIB_FCNTL = @GNULIB_FCNTL@ GNULIB_FDATASYNC = @GNULIB_FDATASYNC@ GNULIB_FDOPEN = @GNULIB_FDOPEN@ GNULIB_FFLUSH = @GNULIB_FFLUSH@ GNULIB_FFSL = @GNULIB_FFSL@ GNULIB_FFSLL = @GNULIB_FFSLL@ GNULIB_FGETC = @GNULIB_FGETC@ GNULIB_FGETS = @GNULIB_FGETS@ GNULIB_FNMATCH = @GNULIB_FNMATCH@ GNULIB_FOPEN = @GNULIB_FOPEN@ GNULIB_FPRINTF = @GNULIB_FPRINTF@ GNULIB_FPRINTF_POSIX = @GNULIB_FPRINTF_POSIX@ GNULIB_FPURGE = @GNULIB_FPURGE@ GNULIB_FPUTC = @GNULIB_FPUTC@ GNULIB_FPUTS = @GNULIB_FPUTS@ GNULIB_FREAD = @GNULIB_FREAD@ GNULIB_FREE_POSIX = @GNULIB_FREE_POSIX@ GNULIB_FREOPEN = @GNULIB_FREOPEN@ GNULIB_FSCANF = @GNULIB_FSCANF@ GNULIB_FSEEK = @GNULIB_FSEEK@ GNULIB_FSEEKO = @GNULIB_FSEEKO@ GNULIB_FSTAT = @GNULIB_FSTAT@ GNULIB_FSTATAT = @GNULIB_FSTATAT@ GNULIB_FSYNC = @GNULIB_FSYNC@ GNULIB_FTELL = @GNULIB_FTELL@ GNULIB_FTELLO = @GNULIB_FTELLO@ GNULIB_FTRUNCATE = @GNULIB_FTRUNCATE@ GNULIB_FUTIMENS = @GNULIB_FUTIMENS@ GNULIB_FWRITE = @GNULIB_FWRITE@ GNULIB_GETC = @GNULIB_GETC@ GNULIB_GETCHAR = @GNULIB_GETCHAR@ GNULIB_GETCWD = @GNULIB_GETCWD@ GNULIB_GETDELIM = @GNULIB_GETDELIM@ GNULIB_GETDOMAINNAME = @GNULIB_GETDOMAINNAME@ GNULIB_GETDTABLESIZE = @GNULIB_GETDTABLESIZE@ GNULIB_GETENTROPY = @GNULIB_GETENTROPY@ GNULIB_GETGROUPS = @GNULIB_GETGROUPS@ GNULIB_GETHOSTNAME = @GNULIB_GETHOSTNAME@ GNULIB_GETLINE = @GNULIB_GETLINE@ GNULIB_GETLOADAVG = @GNULIB_GETLOADAVG@ GNULIB_GETLOGIN = @GNULIB_GETLOGIN@ GNULIB_GETLOGIN_R = @GNULIB_GETLOGIN_R@ GNULIB_GETOPT_POSIX = @GNULIB_GETOPT_POSIX@ GNULIB_GETPAGESIZE = @GNULIB_GETPAGESIZE@ GNULIB_GETPASS = @GNULIB_GETPASS@ GNULIB_GETPEERNAME = @GNULIB_GETPEERNAME@ GNULIB_GETRANDOM = @GNULIB_GETRANDOM@ GNULIB_GETSOCKNAME = @GNULIB_GETSOCKNAME@ GNULIB_GETSOCKOPT = @GNULIB_GETSOCKOPT@ GNULIB_GETSUBOPT = @GNULIB_GETSUBOPT@ GNULIB_GETTIMEOFDAY = @GNULIB_GETTIMEOFDAY@ GNULIB_GETUMASK = @GNULIB_GETUMASK@ GNULIB_GETUSERSHELL = @GNULIB_GETUSERSHELL@ GNULIB_GL_UNISTD_H_GETOPT = @GNULIB_GL_UNISTD_H_GETOPT@ GNULIB_GRANTPT = @GNULIB_GRANTPT@ GNULIB_GROUP_MEMBER = @GNULIB_GROUP_MEMBER@ GNULIB_IMAXABS = @GNULIB_IMAXABS@ GNULIB_IMAXDIV = @GNULIB_IMAXDIV@ GNULIB_INET_NTOP = @GNULIB_INET_NTOP@ GNULIB_INET_PTON = @GNULIB_INET_PTON@ GNULIB_IOCTL = @GNULIB_IOCTL@ GNULIB_ISATTY = @GNULIB_ISATTY@ GNULIB_ISBLANK = @GNULIB_ISBLANK@ GNULIB_ISWBLANK = @GNULIB_ISWBLANK@ GNULIB_ISWCTYPE = @GNULIB_ISWCTYPE@ GNULIB_ISWDIGIT = @GNULIB_ISWDIGIT@ GNULIB_ISWXDIGIT = @GNULIB_ISWXDIGIT@ GNULIB_LCHMOD = @GNULIB_LCHMOD@ GNULIB_LCHOWN = @GNULIB_LCHOWN@ GNULIB_LINK = @GNULIB_LINK@ GNULIB_LINKAT = @GNULIB_LINKAT@ GNULIB_LISTEN = @GNULIB_LISTEN@ GNULIB_LOCALECONV = @GNULIB_LOCALECONV@ GNULIB_LOCALENAME = @GNULIB_LOCALENAME@ GNULIB_LOCALTIME = @GNULIB_LOCALTIME@ GNULIB_LSEEK = @GNULIB_LSEEK@ GNULIB_LSTAT = @GNULIB_LSTAT@ GNULIB_MALLOC_POSIX = @GNULIB_MALLOC_POSIX@ GNULIB_MBRLEN = @GNULIB_MBRLEN@ GNULIB_MBRTOWC = @GNULIB_MBRTOWC@ GNULIB_MBSCASECMP = @GNULIB_MBSCASECMP@ GNULIB_MBSCASESTR = @GNULIB_MBSCASESTR@ GNULIB_MBSCHR = @GNULIB_MBSCHR@ GNULIB_MBSCSPN = @GNULIB_MBSCSPN@ GNULIB_MBSINIT = @GNULIB_MBSINIT@ GNULIB_MBSLEN = @GNULIB_MBSLEN@ GNULIB_MBSNCASECMP = @GNULIB_MBSNCASECMP@ GNULIB_MBSNLEN = @GNULIB_MBSNLEN@ GNULIB_MBSNRTOWCS = @GNULIB_MBSNRTOWCS@ GNULIB_MBSPBRK = @GNULIB_MBSPBRK@ GNULIB_MBSPCASECMP = @GNULIB_MBSPCASECMP@ GNULIB_MBSRCHR = @GNULIB_MBSRCHR@ GNULIB_MBSRTOWCS = @GNULIB_MBSRTOWCS@ GNULIB_MBSSEP = @GNULIB_MBSSEP@ GNULIB_MBSSPN = @GNULIB_MBSSPN@ GNULIB_MBSSTR = @GNULIB_MBSSTR@ GNULIB_MBSTOK_R = @GNULIB_MBSTOK_R@ GNULIB_MBTOWC = @GNULIB_MBTOWC@ GNULIB_MDA_ACCESS = @GNULIB_MDA_ACCESS@ GNULIB_MDA_CHDIR = @GNULIB_MDA_CHDIR@ GNULIB_MDA_CHMOD = @GNULIB_MDA_CHMOD@ GNULIB_MDA_CLOSE = @GNULIB_MDA_CLOSE@ GNULIB_MDA_CREAT = @GNULIB_MDA_CREAT@ GNULIB_MDA_DUP = @GNULIB_MDA_DUP@ GNULIB_MDA_DUP2 = @GNULIB_MDA_DUP2@ GNULIB_MDA_ECVT = @GNULIB_MDA_ECVT@ GNULIB_MDA_EXECL = @GNULIB_MDA_EXECL@ GNULIB_MDA_EXECLE = @GNULIB_MDA_EXECLE@ GNULIB_MDA_EXECLP = @GNULIB_MDA_EXECLP@ GNULIB_MDA_EXECV = @GNULIB_MDA_EXECV@ GNULIB_MDA_EXECVE = @GNULIB_MDA_EXECVE@ GNULIB_MDA_EXECVP = @GNULIB_MDA_EXECVP@ GNULIB_MDA_EXECVPE = @GNULIB_MDA_EXECVPE@ GNULIB_MDA_FCLOSEALL = @GNULIB_MDA_FCLOSEALL@ GNULIB_MDA_FCVT = @GNULIB_MDA_FCVT@ GNULIB_MDA_FDOPEN = @GNULIB_MDA_FDOPEN@ GNULIB_MDA_FILENO = @GNULIB_MDA_FILENO@ GNULIB_MDA_GCVT = @GNULIB_MDA_GCVT@ GNULIB_MDA_GETCWD = @GNULIB_MDA_GETCWD@ GNULIB_MDA_GETPID = @GNULIB_MDA_GETPID@ GNULIB_MDA_GETW = @GNULIB_MDA_GETW@ GNULIB_MDA_ISATTY = @GNULIB_MDA_ISATTY@ GNULIB_MDA_LSEEK = @GNULIB_MDA_LSEEK@ GNULIB_MDA_MEMCCPY = @GNULIB_MDA_MEMCCPY@ GNULIB_MDA_MKDIR = @GNULIB_MDA_MKDIR@ GNULIB_MDA_MKTEMP = @GNULIB_MDA_MKTEMP@ GNULIB_MDA_OPEN = @GNULIB_MDA_OPEN@ GNULIB_MDA_PUTENV = @GNULIB_MDA_PUTENV@ GNULIB_MDA_PUTW = @GNULIB_MDA_PUTW@ GNULIB_MDA_READ = @GNULIB_MDA_READ@ GNULIB_MDA_RMDIR = @GNULIB_MDA_RMDIR@ GNULIB_MDA_STRDUP = @GNULIB_MDA_STRDUP@ GNULIB_MDA_SWAB = @GNULIB_MDA_SWAB@ GNULIB_MDA_TEMPNAM = @GNULIB_MDA_TEMPNAM@ GNULIB_MDA_TZSET = @GNULIB_MDA_TZSET@ GNULIB_MDA_UMASK = @GNULIB_MDA_UMASK@ GNULIB_MDA_UNLINK = @GNULIB_MDA_UNLINK@ GNULIB_MDA_WCSDUP = @GNULIB_MDA_WCSDUP@ GNULIB_MDA_WRITE = @GNULIB_MDA_WRITE@ GNULIB_MEMCHR = @GNULIB_MEMCHR@ GNULIB_MEMMEM = @GNULIB_MEMMEM@ GNULIB_MEMPCPY = @GNULIB_MEMPCPY@ GNULIB_MEMRCHR = @GNULIB_MEMRCHR@ GNULIB_MKDIR = @GNULIB_MKDIR@ GNULIB_MKDIRAT = @GNULIB_MKDIRAT@ GNULIB_MKDTEMP = @GNULIB_MKDTEMP@ GNULIB_MKFIFO = @GNULIB_MKFIFO@ GNULIB_MKFIFOAT = @GNULIB_MKFIFOAT@ GNULIB_MKNOD = @GNULIB_MKNOD@ GNULIB_MKNODAT = @GNULIB_MKNODAT@ GNULIB_MKOSTEMP = @GNULIB_MKOSTEMP@ GNULIB_MKOSTEMPS = @GNULIB_MKOSTEMPS@ GNULIB_MKSTEMP = @GNULIB_MKSTEMP@ GNULIB_MKSTEMPS = @GNULIB_MKSTEMPS@ GNULIB_MKTIME = @GNULIB_MKTIME@ GNULIB_NANOSLEEP = @GNULIB_NANOSLEEP@ GNULIB_NL_LANGINFO = @GNULIB_NL_LANGINFO@ GNULIB_NONBLOCKING = @GNULIB_NONBLOCKING@ GNULIB_OBSTACK_PRINTF = @GNULIB_OBSTACK_PRINTF@ GNULIB_OBSTACK_PRINTF_POSIX = @GNULIB_OBSTACK_PRINTF_POSIX@ GNULIB_OPEN = @GNULIB_OPEN@ GNULIB_OPENAT = @GNULIB_OPENAT@ GNULIB_OVERRIDES_STRUCT_STAT = @GNULIB_OVERRIDES_STRUCT_STAT@ GNULIB_OVERRIDES_WINT_T = @GNULIB_OVERRIDES_WINT_T@ GNULIB_PCLOSE = @GNULIB_PCLOSE@ GNULIB_PERROR = @GNULIB_PERROR@ GNULIB_PIPE = @GNULIB_PIPE@ GNULIB_PIPE2 = @GNULIB_PIPE2@ GNULIB_POPEN = @GNULIB_POPEN@ GNULIB_POSIX_MEMALIGN = @GNULIB_POSIX_MEMALIGN@ GNULIB_POSIX_OPENPT = @GNULIB_POSIX_OPENPT@ GNULIB_PREAD = @GNULIB_PREAD@ GNULIB_PRINTF = @GNULIB_PRINTF@ GNULIB_PRINTF_POSIX = @GNULIB_PRINTF_POSIX@ GNULIB_PSELECT = @GNULIB_PSELECT@ GNULIB_PTHREAD_COND = @GNULIB_PTHREAD_COND@ GNULIB_PTHREAD_MUTEX = @GNULIB_PTHREAD_MUTEX@ GNULIB_PTHREAD_MUTEX_TIMEDLOCK = @GNULIB_PTHREAD_MUTEX_TIMEDLOCK@ GNULIB_PTHREAD_ONCE = @GNULIB_PTHREAD_ONCE@ GNULIB_PTHREAD_RWLOCK = @GNULIB_PTHREAD_RWLOCK@ GNULIB_PTHREAD_SIGMASK = @GNULIB_PTHREAD_SIGMASK@ GNULIB_PTHREAD_SPIN = @GNULIB_PTHREAD_SPIN@ GNULIB_PTHREAD_THREAD = @GNULIB_PTHREAD_THREAD@ GNULIB_PTHREAD_TSS = @GNULIB_PTHREAD_TSS@ GNULIB_PTSNAME = @GNULIB_PTSNAME@ GNULIB_PTSNAME_R = @GNULIB_PTSNAME_R@ GNULIB_PUTC = @GNULIB_PUTC@ GNULIB_PUTCHAR = @GNULIB_PUTCHAR@ GNULIB_PUTENV = @GNULIB_PUTENV@ GNULIB_PUTS = @GNULIB_PUTS@ GNULIB_PWRITE = @GNULIB_PWRITE@ GNULIB_QSORT_R = @GNULIB_QSORT_R@ GNULIB_RAISE = @GNULIB_RAISE@ GNULIB_RANDOM = @GNULIB_RANDOM@ GNULIB_RANDOM_R = @GNULIB_RANDOM_R@ GNULIB_RAWMEMCHR = @GNULIB_RAWMEMCHR@ GNULIB_READ = @GNULIB_READ@ GNULIB_READLINK = @GNULIB_READLINK@ GNULIB_READLINKAT = @GNULIB_READLINKAT@ GNULIB_REALLOCARRAY = @GNULIB_REALLOCARRAY@ GNULIB_REALLOC_POSIX = @GNULIB_REALLOC_POSIX@ GNULIB_REALPATH = @GNULIB_REALPATH@ GNULIB_RECV = @GNULIB_RECV@ GNULIB_RECVFROM = @GNULIB_RECVFROM@ GNULIB_REMOVE = @GNULIB_REMOVE@ GNULIB_RENAME = @GNULIB_RENAME@ GNULIB_RENAMEAT = @GNULIB_RENAMEAT@ GNULIB_RMDIR = @GNULIB_RMDIR@ GNULIB_RPMATCH = @GNULIB_RPMATCH@ GNULIB_SCANF = @GNULIB_SCANF@ GNULIB_SCHED_YIELD = @GNULIB_SCHED_YIELD@ GNULIB_SECURE_GETENV = @GNULIB_SECURE_GETENV@ GNULIB_SELECT = @GNULIB_SELECT@ GNULIB_SEND = @GNULIB_SEND@ GNULIB_SENDTO = @GNULIB_SENDTO@ GNULIB_SETENV = @GNULIB_SETENV@ GNULIB_SETHOSTNAME = @GNULIB_SETHOSTNAME@ GNULIB_SETLOCALE = @GNULIB_SETLOCALE@ GNULIB_SETLOCALE_NULL = @GNULIB_SETLOCALE_NULL@ GNULIB_SETSOCKOPT = @GNULIB_SETSOCKOPT@ GNULIB_SHUTDOWN = @GNULIB_SHUTDOWN@ GNULIB_SIGABBREV_NP = @GNULIB_SIGABBREV_NP@ GNULIB_SIGACTION = @GNULIB_SIGACTION@ GNULIB_SIGDESCR_NP = @GNULIB_SIGDESCR_NP@ GNULIB_SIGNAL_H_SIGPIPE = @GNULIB_SIGNAL_H_SIGPIPE@ GNULIB_SIGPROCMASK = @GNULIB_SIGPROCMASK@ GNULIB_SLEEP = @GNULIB_SLEEP@ GNULIB_SNPRINTF = @GNULIB_SNPRINTF@ GNULIB_SOCKET = @GNULIB_SOCKET@ GNULIB_SPRINTF_POSIX = @GNULIB_SPRINTF_POSIX@ GNULIB_STAT = @GNULIB_STAT@ GNULIB_STDIO_H_NONBLOCKING = @GNULIB_STDIO_H_NONBLOCKING@ GNULIB_STDIO_H_SIGPIPE = @GNULIB_STDIO_H_SIGPIPE@ GNULIB_STPCPY = @GNULIB_STPCPY@ GNULIB_STPNCPY = @GNULIB_STPNCPY@ GNULIB_STRCASESTR = @GNULIB_STRCASESTR@ GNULIB_STRCHRNUL = @GNULIB_STRCHRNUL@ GNULIB_STRDUP = @GNULIB_STRDUP@ GNULIB_STRERROR = @GNULIB_STRERROR@ GNULIB_STRERRORNAME_NP = @GNULIB_STRERRORNAME_NP@ GNULIB_STRERROR_R = @GNULIB_STRERROR_R@ GNULIB_STRFTIME = @GNULIB_STRFTIME@ GNULIB_STRNCAT = @GNULIB_STRNCAT@ GNULIB_STRNDUP = @GNULIB_STRNDUP@ GNULIB_STRNLEN = @GNULIB_STRNLEN@ GNULIB_STRPBRK = @GNULIB_STRPBRK@ GNULIB_STRPTIME = @GNULIB_STRPTIME@ GNULIB_STRSEP = @GNULIB_STRSEP@ GNULIB_STRSIGNAL = @GNULIB_STRSIGNAL@ GNULIB_STRSTR = @GNULIB_STRSTR@ GNULIB_STRTOD = @GNULIB_STRTOD@ GNULIB_STRTOIMAX = @GNULIB_STRTOIMAX@ GNULIB_STRTOK_R = @GNULIB_STRTOK_R@ GNULIB_STRTOLD = @GNULIB_STRTOLD@ GNULIB_STRTOLL = @GNULIB_STRTOLL@ GNULIB_STRTOULL = @GNULIB_STRTOULL@ GNULIB_STRTOUMAX = @GNULIB_STRTOUMAX@ GNULIB_STRVERSCMP = @GNULIB_STRVERSCMP@ GNULIB_SYMLINK = @GNULIB_SYMLINK@ GNULIB_SYMLINKAT = @GNULIB_SYMLINKAT@ GNULIB_SYSTEM_POSIX = @GNULIB_SYSTEM_POSIX@ GNULIB_TIMEGM = @GNULIB_TIMEGM@ GNULIB_TIMESPEC_GET = @GNULIB_TIMESPEC_GET@ GNULIB_TIME_R = @GNULIB_TIME_R@ GNULIB_TIME_RZ = @GNULIB_TIME_RZ@ GNULIB_TMPFILE = @GNULIB_TMPFILE@ GNULIB_TOWCTRANS = @GNULIB_TOWCTRANS@ GNULIB_TRUNCATE = @GNULIB_TRUNCATE@ GNULIB_TTYNAME_R = @GNULIB_TTYNAME_R@ GNULIB_TZSET = @GNULIB_TZSET@ GNULIB_UNISTD_H_NONBLOCKING = @GNULIB_UNISTD_H_NONBLOCKING@ GNULIB_UNISTD_H_SIGPIPE = @GNULIB_UNISTD_H_SIGPIPE@ GNULIB_UNLINK = @GNULIB_UNLINK@ GNULIB_UNLINKAT = @GNULIB_UNLINKAT@ GNULIB_UNLOCKPT = @GNULIB_UNLOCKPT@ GNULIB_UNSETENV = @GNULIB_UNSETENV@ GNULIB_USLEEP = @GNULIB_USLEEP@ GNULIB_UTIMENSAT = @GNULIB_UTIMENSAT@ GNULIB_VASPRINTF = @GNULIB_VASPRINTF@ GNULIB_VDPRINTF = @GNULIB_VDPRINTF@ GNULIB_VFPRINTF = @GNULIB_VFPRINTF@ GNULIB_VFPRINTF_POSIX = @GNULIB_VFPRINTF_POSIX@ GNULIB_VFSCANF = @GNULIB_VFSCANF@ GNULIB_VPRINTF = @GNULIB_VPRINTF@ GNULIB_VPRINTF_POSIX = @GNULIB_VPRINTF_POSIX@ GNULIB_VSCANF = @GNULIB_VSCANF@ GNULIB_VSNPRINTF = @GNULIB_VSNPRINTF@ GNULIB_VSPRINTF_POSIX = @GNULIB_VSPRINTF_POSIX@ GNULIB_WAITPID = @GNULIB_WAITPID@ GNULIB_WCPCPY = @GNULIB_WCPCPY@ GNULIB_WCPNCPY = @GNULIB_WCPNCPY@ GNULIB_WCRTOMB = @GNULIB_WCRTOMB@ GNULIB_WCSCASECMP = @GNULIB_WCSCASECMP@ GNULIB_WCSCAT = @GNULIB_WCSCAT@ GNULIB_WCSCHR = @GNULIB_WCSCHR@ GNULIB_WCSCMP = @GNULIB_WCSCMP@ GNULIB_WCSCOLL = @GNULIB_WCSCOLL@ GNULIB_WCSCPY = @GNULIB_WCSCPY@ GNULIB_WCSCSPN = @GNULIB_WCSCSPN@ GNULIB_WCSDUP = @GNULIB_WCSDUP@ GNULIB_WCSFTIME = @GNULIB_WCSFTIME@ GNULIB_WCSLEN = @GNULIB_WCSLEN@ GNULIB_WCSNCASECMP = @GNULIB_WCSNCASECMP@ GNULIB_WCSNCAT = @GNULIB_WCSNCAT@ GNULIB_WCSNCMP = @GNULIB_WCSNCMP@ GNULIB_WCSNCPY = @GNULIB_WCSNCPY@ GNULIB_WCSNLEN = @GNULIB_WCSNLEN@ GNULIB_WCSNRTOMBS = @GNULIB_WCSNRTOMBS@ GNULIB_WCSPBRK = @GNULIB_WCSPBRK@ GNULIB_WCSRCHR = @GNULIB_WCSRCHR@ GNULIB_WCSRTOMBS = @GNULIB_WCSRTOMBS@ GNULIB_WCSSPN = @GNULIB_WCSSPN@ GNULIB_WCSSTR = @GNULIB_WCSSTR@ GNULIB_WCSTOK = @GNULIB_WCSTOK@ GNULIB_WCSWIDTH = @GNULIB_WCSWIDTH@ GNULIB_WCSXFRM = @GNULIB_WCSXFRM@ GNULIB_WCTOB = @GNULIB_WCTOB@ GNULIB_WCTOMB = @GNULIB_WCTOMB@ GNULIB_WCTRANS = @GNULIB_WCTRANS@ GNULIB_WCTYPE = @GNULIB_WCTYPE@ GNULIB_WCWIDTH = @GNULIB_WCWIDTH@ GNULIB_WMEMCHR = @GNULIB_WMEMCHR@ GNULIB_WMEMCMP = @GNULIB_WMEMCMP@ GNULIB_WMEMCPY = @GNULIB_WMEMCPY@ GNULIB_WMEMMOVE = @GNULIB_WMEMMOVE@ GNULIB_WMEMPCPY = @GNULIB_WMEMPCPY@ GNULIB_WMEMSET = @GNULIB_WMEMSET@ GNULIB_WRITE = @GNULIB_WRITE@ GNULIB__EXIT = @GNULIB__EXIT@ GREP = @GREP@ HAVE_ACCEPT4 = @HAVE_ACCEPT4@ HAVE_ALIGNED_ALLOC = @HAVE_ALIGNED_ALLOC@ HAVE_ALLOCA_H = @HAVE_ALLOCA_H@ HAVE_ARPA_INET_H = @HAVE_ARPA_INET_H@ HAVE_ATOLL = @HAVE_ATOLL@ HAVE_BTOWC = @HAVE_BTOWC@ HAVE_C99_STDINT_H = @HAVE_C99_STDINT_H@ HAVE_CANONICALIZE_FILE_NAME = @HAVE_CANONICALIZE_FILE_NAME@ HAVE_CHOWN = @HAVE_CHOWN@ HAVE_COPY_FILE_RANGE = @HAVE_COPY_FILE_RANGE@ HAVE_CRTDEFS_H = @HAVE_CRTDEFS_H@ HAVE_DECL_ECVT = @HAVE_DECL_ECVT@ HAVE_DECL_ENVIRON = @HAVE_DECL_ENVIRON@ HAVE_DECL_EXECVPE = @HAVE_DECL_EXECVPE@ HAVE_DECL_FCHDIR = @HAVE_DECL_FCHDIR@ HAVE_DECL_FCLOSEALL = @HAVE_DECL_FCLOSEALL@ HAVE_DECL_FCVT = @HAVE_DECL_FCVT@ HAVE_DECL_FDATASYNC = @HAVE_DECL_FDATASYNC@ HAVE_DECL_FPURGE = @HAVE_DECL_FPURGE@ HAVE_DECL_FSEEKO = @HAVE_DECL_FSEEKO@ HAVE_DECL_FTELLO = @HAVE_DECL_FTELLO@ HAVE_DECL_GCVT = @HAVE_DECL_GCVT@ HAVE_DECL_GETDELIM = @HAVE_DECL_GETDELIM@ HAVE_DECL_GETDOMAINNAME = @HAVE_DECL_GETDOMAINNAME@ HAVE_DECL_GETLINE = @HAVE_DECL_GETLINE@ HAVE_DECL_GETLOADAVG = @HAVE_DECL_GETLOADAVG@ HAVE_DECL_GETLOGIN = @HAVE_DECL_GETLOGIN@ HAVE_DECL_GETLOGIN_R = @HAVE_DECL_GETLOGIN_R@ HAVE_DECL_GETPAGESIZE = @HAVE_DECL_GETPAGESIZE@ HAVE_DECL_GETUSERSHELL = @HAVE_DECL_GETUSERSHELL@ HAVE_DECL_IMAXABS = @HAVE_DECL_IMAXABS@ HAVE_DECL_IMAXDIV = @HAVE_DECL_IMAXDIV@ HAVE_DECL_INET_NTOP = @HAVE_DECL_INET_NTOP@ HAVE_DECL_INET_PTON = @HAVE_DECL_INET_PTON@ HAVE_DECL_INITSTATE = @HAVE_DECL_INITSTATE@ HAVE_DECL_LOCALTIME_R = @HAVE_DECL_LOCALTIME_R@ HAVE_DECL_MEMMEM = @HAVE_DECL_MEMMEM@ HAVE_DECL_MEMRCHR = @HAVE_DECL_MEMRCHR@ HAVE_DECL_OBSTACK_PRINTF = @HAVE_DECL_OBSTACK_PRINTF@ HAVE_DECL_SETENV = @HAVE_DECL_SETENV@ HAVE_DECL_SETHOSTNAME = @HAVE_DECL_SETHOSTNAME@ HAVE_DECL_SETSTATE = @HAVE_DECL_SETSTATE@ HAVE_DECL_SNPRINTF = @HAVE_DECL_SNPRINTF@ HAVE_DECL_STRDUP = @HAVE_DECL_STRDUP@ HAVE_DECL_STRERROR_R = @HAVE_DECL_STRERROR_R@ HAVE_DECL_STRNDUP = @HAVE_DECL_STRNDUP@ HAVE_DECL_STRNLEN = @HAVE_DECL_STRNLEN@ HAVE_DECL_STRSIGNAL = @HAVE_DECL_STRSIGNAL@ HAVE_DECL_STRTOIMAX = @HAVE_DECL_STRTOIMAX@ HAVE_DECL_STRTOK_R = @HAVE_DECL_STRTOK_R@ HAVE_DECL_STRTOUMAX = @HAVE_DECL_STRTOUMAX@ HAVE_DECL_TRUNCATE = @HAVE_DECL_TRUNCATE@ HAVE_DECL_TTYNAME_R = @HAVE_DECL_TTYNAME_R@ HAVE_DECL_UNSETENV = @HAVE_DECL_UNSETENV@ HAVE_DECL_VSNPRINTF = @HAVE_DECL_VSNPRINTF@ HAVE_DECL_WCSDUP = @HAVE_DECL_WCSDUP@ HAVE_DECL_WCTOB = @HAVE_DECL_WCTOB@ HAVE_DECL_WCWIDTH = @HAVE_DECL_WCWIDTH@ HAVE_DPRINTF = @HAVE_DPRINTF@ HAVE_DUP3 = @HAVE_DUP3@ HAVE_DUPLOCALE = @HAVE_DUPLOCALE@ HAVE_EUIDACCESS = @HAVE_EUIDACCESS@ HAVE_EXECVPE = @HAVE_EXECVPE@ HAVE_EXPLICIT_BZERO = @HAVE_EXPLICIT_BZERO@ HAVE_FACCESSAT = @HAVE_FACCESSAT@ HAVE_FCHDIR = @HAVE_FCHDIR@ HAVE_FCHMODAT = @HAVE_FCHMODAT@ HAVE_FCHOWNAT = @HAVE_FCHOWNAT@ HAVE_FCNTL = @HAVE_FCNTL@ HAVE_FDATASYNC = @HAVE_FDATASYNC@ HAVE_FEATURES_H = @HAVE_FEATURES_H@ HAVE_FFSL = @HAVE_FFSL@ HAVE_FFSLL = @HAVE_FFSLL@ HAVE_FNMATCH = @HAVE_FNMATCH@ HAVE_FNMATCH_H = @HAVE_FNMATCH_H@ HAVE_FREELOCALE = @HAVE_FREELOCALE@ HAVE_FSEEKO = @HAVE_FSEEKO@ HAVE_FSTATAT = @HAVE_FSTATAT@ HAVE_FSYNC = @HAVE_FSYNC@ HAVE_FTELLO = @HAVE_FTELLO@ HAVE_FTRUNCATE = @HAVE_FTRUNCATE@ HAVE_FUTIMENS = @HAVE_FUTIMENS@ HAVE_GETDTABLESIZE = @HAVE_GETDTABLESIZE@ HAVE_GETENTROPY = @HAVE_GETENTROPY@ HAVE_GETGROUPS = @HAVE_GETGROUPS@ HAVE_GETHOSTNAME = @HAVE_GETHOSTNAME@ HAVE_GETLOGIN = @HAVE_GETLOGIN@ HAVE_GETOPT_H = @HAVE_GETOPT_H@ HAVE_GETPAGESIZE = @HAVE_GETPAGESIZE@ HAVE_GETPASS = @HAVE_GETPASS@ HAVE_GETRANDOM = @HAVE_GETRANDOM@ HAVE_GETSUBOPT = @HAVE_GETSUBOPT@ HAVE_GETTIMEOFDAY = @HAVE_GETTIMEOFDAY@ HAVE_GETUMASK = @HAVE_GETUMASK@ HAVE_GRANTPT = @HAVE_GRANTPT@ HAVE_GROUP_MEMBER = @HAVE_GROUP_MEMBER@ HAVE_IMAXDIV_T = @HAVE_IMAXDIV_T@ HAVE_INITSTATE = @HAVE_INITSTATE@ HAVE_INTTYPES_H = @HAVE_INTTYPES_H@ HAVE_ISBLANK = @HAVE_ISBLANK@ HAVE_ISWBLANK = @HAVE_ISWBLANK@ HAVE_ISWCNTRL = @HAVE_ISWCNTRL@ HAVE_LANGINFO_ALTMON = @HAVE_LANGINFO_ALTMON@ HAVE_LANGINFO_CODESET = @HAVE_LANGINFO_CODESET@ HAVE_LANGINFO_ERA = @HAVE_LANGINFO_ERA@ HAVE_LANGINFO_H = @HAVE_LANGINFO_H@ HAVE_LANGINFO_T_FMT_AMPM = @HAVE_LANGINFO_T_FMT_AMPM@ HAVE_LANGINFO_YESEXPR = @HAVE_LANGINFO_YESEXPR@ HAVE_LCHMOD = @HAVE_LCHMOD@ HAVE_LCHOWN = @HAVE_LCHOWN@ HAVE_LINK = @HAVE_LINK@ HAVE_LINKAT = @HAVE_LINKAT@ HAVE_LSTAT = @HAVE_LSTAT@ HAVE_MAX_ALIGN_T = @HAVE_MAX_ALIGN_T@ HAVE_MBRLEN = @HAVE_MBRLEN@ HAVE_MBRTOWC = @HAVE_MBRTOWC@ HAVE_MBSINIT = @HAVE_MBSINIT@ HAVE_MBSLEN = @HAVE_MBSLEN@ HAVE_MBSNRTOWCS = @HAVE_MBSNRTOWCS@ HAVE_MBSRTOWCS = @HAVE_MBSRTOWCS@ HAVE_MBTOWC = @HAVE_MBTOWC@ HAVE_MEMPCPY = @HAVE_MEMPCPY@ HAVE_MKDIRAT = @HAVE_MKDIRAT@ HAVE_MKDTEMP = @HAVE_MKDTEMP@ HAVE_MKFIFO = @HAVE_MKFIFO@ HAVE_MKFIFOAT = @HAVE_MKFIFOAT@ HAVE_MKNOD = @HAVE_MKNOD@ HAVE_MKNODAT = @HAVE_MKNODAT@ HAVE_MKOSTEMP = @HAVE_MKOSTEMP@ HAVE_MKOSTEMPS = @HAVE_MKOSTEMPS@ HAVE_MKSTEMP = @HAVE_MKSTEMP@ HAVE_MKSTEMPS = @HAVE_MKSTEMPS@ HAVE_MSVC_INVALID_PARAMETER_HANDLER = @HAVE_MSVC_INVALID_PARAMETER_HANDLER@ HAVE_NANOSLEEP = @HAVE_NANOSLEEP@ HAVE_NETINET_IN_H = @HAVE_NETINET_IN_H@ HAVE_NEWLOCALE = @HAVE_NEWLOCALE@ HAVE_NL_LANGINFO = @HAVE_NL_LANGINFO@ HAVE_OPENAT = @HAVE_OPENAT@ HAVE_OS_H = @HAVE_OS_H@ HAVE_PCLOSE = @HAVE_PCLOSE@ HAVE_PIPE = @HAVE_PIPE@ HAVE_PIPE2 = @HAVE_PIPE2@ HAVE_POPEN = @HAVE_POPEN@ HAVE_POSIX_MEMALIGN = @HAVE_POSIX_MEMALIGN@ HAVE_POSIX_OPENPT = @HAVE_POSIX_OPENPT@ HAVE_POSIX_SIGNALBLOCKING = @HAVE_POSIX_SIGNALBLOCKING@ HAVE_PREAD = @HAVE_PREAD@ HAVE_PSELECT = @HAVE_PSELECT@ HAVE_PTHREAD_ATTR_DESTROY = @HAVE_PTHREAD_ATTR_DESTROY@ HAVE_PTHREAD_ATTR_GETDETACHSTATE = @HAVE_PTHREAD_ATTR_GETDETACHSTATE@ HAVE_PTHREAD_ATTR_INIT = @HAVE_PTHREAD_ATTR_INIT@ HAVE_PTHREAD_ATTR_SETDETACHSTATE = @HAVE_PTHREAD_ATTR_SETDETACHSTATE@ HAVE_PTHREAD_CONDATTR_DESTROY = @HAVE_PTHREAD_CONDATTR_DESTROY@ HAVE_PTHREAD_CONDATTR_INIT = @HAVE_PTHREAD_CONDATTR_INIT@ HAVE_PTHREAD_COND_BROADCAST = @HAVE_PTHREAD_COND_BROADCAST@ HAVE_PTHREAD_COND_DESTROY = @HAVE_PTHREAD_COND_DESTROY@ HAVE_PTHREAD_COND_INIT = @HAVE_PTHREAD_COND_INIT@ HAVE_PTHREAD_COND_SIGNAL = @HAVE_PTHREAD_COND_SIGNAL@ HAVE_PTHREAD_COND_TIMEDWAIT = @HAVE_PTHREAD_COND_TIMEDWAIT@ HAVE_PTHREAD_COND_WAIT = @HAVE_PTHREAD_COND_WAIT@ HAVE_PTHREAD_CREATE = @HAVE_PTHREAD_CREATE@ HAVE_PTHREAD_CREATE_DETACHED = @HAVE_PTHREAD_CREATE_DETACHED@ HAVE_PTHREAD_DETACH = @HAVE_PTHREAD_DETACH@ HAVE_PTHREAD_EQUAL = @HAVE_PTHREAD_EQUAL@ HAVE_PTHREAD_EXIT = @HAVE_PTHREAD_EXIT@ HAVE_PTHREAD_GETSPECIFIC = @HAVE_PTHREAD_GETSPECIFIC@ HAVE_PTHREAD_H = @HAVE_PTHREAD_H@ HAVE_PTHREAD_JOIN = @HAVE_PTHREAD_JOIN@ HAVE_PTHREAD_KEY_CREATE = @HAVE_PTHREAD_KEY_CREATE@ HAVE_PTHREAD_KEY_DELETE = @HAVE_PTHREAD_KEY_DELETE@ HAVE_PTHREAD_MUTEXATTR_DESTROY = @HAVE_PTHREAD_MUTEXATTR_DESTROY@ HAVE_PTHREAD_MUTEXATTR_GETROBUST = @HAVE_PTHREAD_MUTEXATTR_GETROBUST@ HAVE_PTHREAD_MUTEXATTR_GETTYPE = @HAVE_PTHREAD_MUTEXATTR_GETTYPE@ HAVE_PTHREAD_MUTEXATTR_INIT = @HAVE_PTHREAD_MUTEXATTR_INIT@ HAVE_PTHREAD_MUTEXATTR_SETROBUST = @HAVE_PTHREAD_MUTEXATTR_SETROBUST@ HAVE_PTHREAD_MUTEXATTR_SETTYPE = @HAVE_PTHREAD_MUTEXATTR_SETTYPE@ HAVE_PTHREAD_MUTEX_DESTROY = @HAVE_PTHREAD_MUTEX_DESTROY@ HAVE_PTHREAD_MUTEX_INIT = @HAVE_PTHREAD_MUTEX_INIT@ HAVE_PTHREAD_MUTEX_LOCK = @HAVE_PTHREAD_MUTEX_LOCK@ HAVE_PTHREAD_MUTEX_RECURSIVE = @HAVE_PTHREAD_MUTEX_RECURSIVE@ HAVE_PTHREAD_MUTEX_ROBUST = @HAVE_PTHREAD_MUTEX_ROBUST@ HAVE_PTHREAD_MUTEX_TIMEDLOCK = @HAVE_PTHREAD_MUTEX_TIMEDLOCK@ HAVE_PTHREAD_MUTEX_TRYLOCK = @HAVE_PTHREAD_MUTEX_TRYLOCK@ HAVE_PTHREAD_MUTEX_UNLOCK = @HAVE_PTHREAD_MUTEX_UNLOCK@ HAVE_PTHREAD_ONCE = @HAVE_PTHREAD_ONCE@ HAVE_PTHREAD_PROCESS_SHARED = @HAVE_PTHREAD_PROCESS_SHARED@ HAVE_PTHREAD_RWLOCKATTR_DESTROY = @HAVE_PTHREAD_RWLOCKATTR_DESTROY@ HAVE_PTHREAD_RWLOCKATTR_INIT = @HAVE_PTHREAD_RWLOCKATTR_INIT@ HAVE_PTHREAD_RWLOCK_DESTROY = @HAVE_PTHREAD_RWLOCK_DESTROY@ HAVE_PTHREAD_RWLOCK_INIT = @HAVE_PTHREAD_RWLOCK_INIT@ HAVE_PTHREAD_RWLOCK_RDLOCK = @HAVE_PTHREAD_RWLOCK_RDLOCK@ HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK = @HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK@ HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK = @HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK@ HAVE_PTHREAD_RWLOCK_TRYRDLOCK = @HAVE_PTHREAD_RWLOCK_TRYRDLOCK@ HAVE_PTHREAD_RWLOCK_TRYWRLOCK = @HAVE_PTHREAD_RWLOCK_TRYWRLOCK@ HAVE_PTHREAD_RWLOCK_UNLOCK = @HAVE_PTHREAD_RWLOCK_UNLOCK@ HAVE_PTHREAD_RWLOCK_WRLOCK = @HAVE_PTHREAD_RWLOCK_WRLOCK@ HAVE_PTHREAD_SELF = @HAVE_PTHREAD_SELF@ HAVE_PTHREAD_SETSPECIFIC = @HAVE_PTHREAD_SETSPECIFIC@ HAVE_PTHREAD_SIGMASK = @HAVE_PTHREAD_SIGMASK@ HAVE_PTHREAD_SPINLOCK_T = @HAVE_PTHREAD_SPINLOCK_T@ HAVE_PTHREAD_SPIN_DESTROY = @HAVE_PTHREAD_SPIN_DESTROY@ HAVE_PTHREAD_SPIN_INIT = @HAVE_PTHREAD_SPIN_INIT@ HAVE_PTHREAD_SPIN_LOCK = @HAVE_PTHREAD_SPIN_LOCK@ HAVE_PTHREAD_SPIN_TRYLOCK = @HAVE_PTHREAD_SPIN_TRYLOCK@ HAVE_PTHREAD_SPIN_UNLOCK = @HAVE_PTHREAD_SPIN_UNLOCK@ HAVE_PTHREAD_T = @HAVE_PTHREAD_T@ HAVE_PTSNAME = @HAVE_PTSNAME@ HAVE_PTSNAME_R = @HAVE_PTSNAME_R@ HAVE_PWRITE = @HAVE_PWRITE@ HAVE_QSORT_R = @HAVE_QSORT_R@ HAVE_RAISE = @HAVE_RAISE@ HAVE_RANDOM = @HAVE_RANDOM@ HAVE_RANDOM_H = @HAVE_RANDOM_H@ HAVE_RANDOM_R = @HAVE_RANDOM_R@ HAVE_RAWMEMCHR = @HAVE_RAWMEMCHR@ HAVE_READLINK = @HAVE_READLINK@ HAVE_READLINKAT = @HAVE_READLINKAT@ HAVE_REALLOCARRAY = @HAVE_REALLOCARRAY@ HAVE_REALPATH = @HAVE_REALPATH@ HAVE_RENAMEAT = @HAVE_RENAMEAT@ HAVE_RPMATCH = @HAVE_RPMATCH@ HAVE_SA_FAMILY_T = @HAVE_SA_FAMILY_T@ HAVE_SCHED_H = @HAVE_SCHED_H@ HAVE_SCHED_YIELD = @HAVE_SCHED_YIELD@ HAVE_SECURE_GETENV = @HAVE_SECURE_GETENV@ HAVE_SETENV = @HAVE_SETENV@ HAVE_SETHOSTNAME = @HAVE_SETHOSTNAME@ HAVE_SETSTATE = @HAVE_SETSTATE@ HAVE_SIGABBREV_NP = @HAVE_SIGABBREV_NP@ HAVE_SIGACTION = @HAVE_SIGACTION@ HAVE_SIGDESCR_NP = @HAVE_SIGDESCR_NP@ HAVE_SIGHANDLER_T = @HAVE_SIGHANDLER_T@ HAVE_SIGINFO_T = @HAVE_SIGINFO_T@ HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@ HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@ HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@ HAVE_SIGSET_T = @HAVE_SIGSET_T@ HAVE_SLEEP = @HAVE_SLEEP@ HAVE_STDINT_H = @HAVE_STDINT_H@ HAVE_STPCPY = @HAVE_STPCPY@ HAVE_STPNCPY = @HAVE_STPNCPY@ HAVE_STRCASESTR = @HAVE_STRCASESTR@ HAVE_STRCHRNUL = @HAVE_STRCHRNUL@ HAVE_STRERRORNAME_NP = @HAVE_STRERRORNAME_NP@ HAVE_STRPBRK = @HAVE_STRPBRK@ HAVE_STRPTIME = @HAVE_STRPTIME@ HAVE_STRSEP = @HAVE_STRSEP@ HAVE_STRTOD = @HAVE_STRTOD@ HAVE_STRTOLD = @HAVE_STRTOLD@ HAVE_STRTOLL = @HAVE_STRTOLL@ HAVE_STRTOULL = @HAVE_STRTOULL@ HAVE_STRUCT_RANDOM_DATA = @HAVE_STRUCT_RANDOM_DATA@ HAVE_STRUCT_SCHED_PARAM = @HAVE_STRUCT_SCHED_PARAM@ HAVE_STRUCT_SIGACTION_SA_SIGACTION = @HAVE_STRUCT_SIGACTION_SA_SIGACTION@ HAVE_STRUCT_SOCKADDR_STORAGE = @HAVE_STRUCT_SOCKADDR_STORAGE@ HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY = @HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY@ HAVE_STRUCT_TIMEVAL = @HAVE_STRUCT_TIMEVAL@ HAVE_STRVERSCMP = @HAVE_STRVERSCMP@ HAVE_SYMLINK = @HAVE_SYMLINK@ HAVE_SYMLINKAT = @HAVE_SYMLINKAT@ HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@ HAVE_SYS_CDEFS_H = @HAVE_SYS_CDEFS_H@ HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@ HAVE_SYS_IOCTL_H = @HAVE_SYS_IOCTL_H@ HAVE_SYS_LOADAVG_H = @HAVE_SYS_LOADAVG_H@ HAVE_SYS_PARAM_H = @HAVE_SYS_PARAM_H@ HAVE_SYS_RANDOM_H = @HAVE_SYS_RANDOM_H@ HAVE_SYS_SELECT_H = @HAVE_SYS_SELECT_H@ HAVE_SYS_SOCKET_H = @HAVE_SYS_SOCKET_H@ HAVE_SYS_TIME_H = @HAVE_SYS_TIME_H@ HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@ HAVE_SYS_UIO_H = @HAVE_SYS_UIO_H@ HAVE_TIMEGM = @HAVE_TIMEGM@ HAVE_TIMESPEC_GET = @HAVE_TIMESPEC_GET@ HAVE_TIMEZONE_T = @HAVE_TIMEZONE_T@ HAVE_TYPE_VOLATILE_SIG_ATOMIC_T = @HAVE_TYPE_VOLATILE_SIG_ATOMIC_T@ HAVE_UNISTD_H = @HAVE_UNISTD_H@ HAVE_UNLINKAT = @HAVE_UNLINKAT@ HAVE_UNLOCKPT = @HAVE_UNLOCKPT@ HAVE_USLEEP = @HAVE_USLEEP@ HAVE_UTIMENSAT = @HAVE_UTIMENSAT@ HAVE_VASPRINTF = @HAVE_VASPRINTF@ HAVE_VDPRINTF = @HAVE_VDPRINTF@ HAVE_VISIBILITY = @HAVE_VISIBILITY@ HAVE_WCHAR_H = @HAVE_WCHAR_H@ HAVE_WCHAR_T = @HAVE_WCHAR_T@ HAVE_WCPCPY = @HAVE_WCPCPY@ HAVE_WCPNCPY = @HAVE_WCPNCPY@ HAVE_WCRTOMB = @HAVE_WCRTOMB@ HAVE_WCSCASECMP = @HAVE_WCSCASECMP@ HAVE_WCSCAT = @HAVE_WCSCAT@ HAVE_WCSCHR = @HAVE_WCSCHR@ HAVE_WCSCMP = @HAVE_WCSCMP@ HAVE_WCSCOLL = @HAVE_WCSCOLL@ HAVE_WCSCPY = @HAVE_WCSCPY@ HAVE_WCSCSPN = @HAVE_WCSCSPN@ HAVE_WCSDUP = @HAVE_WCSDUP@ HAVE_WCSFTIME = @HAVE_WCSFTIME@ HAVE_WCSLEN = @HAVE_WCSLEN@ HAVE_WCSNCASECMP = @HAVE_WCSNCASECMP@ HAVE_WCSNCAT = @HAVE_WCSNCAT@ HAVE_WCSNCMP = @HAVE_WCSNCMP@ HAVE_WCSNCPY = @HAVE_WCSNCPY@ HAVE_WCSNLEN = @HAVE_WCSNLEN@ HAVE_WCSNRTOMBS = @HAVE_WCSNRTOMBS@ HAVE_WCSPBRK = @HAVE_WCSPBRK@ HAVE_WCSRCHR = @HAVE_WCSRCHR@ HAVE_WCSRTOMBS = @HAVE_WCSRTOMBS@ HAVE_WCSSPN = @HAVE_WCSSPN@ HAVE_WCSSTR = @HAVE_WCSSTR@ HAVE_WCSTOK = @HAVE_WCSTOK@ HAVE_WCSWIDTH = @HAVE_WCSWIDTH@ HAVE_WCSXFRM = @HAVE_WCSXFRM@ HAVE_WCTRANS_T = @HAVE_WCTRANS_T@ HAVE_WCTYPE_H = @HAVE_WCTYPE_H@ HAVE_WCTYPE_T = @HAVE_WCTYPE_T@ HAVE_WINSOCK2_H = @HAVE_WINSOCK2_H@ HAVE_WINT_T = @HAVE_WINT_T@ HAVE_WMEMCHR = @HAVE_WMEMCHR@ HAVE_WMEMCMP = @HAVE_WMEMCMP@ HAVE_WMEMCPY = @HAVE_WMEMCPY@ HAVE_WMEMMOVE = @HAVE_WMEMMOVE@ HAVE_WMEMPCPY = @HAVE_WMEMPCPY@ HAVE_WMEMSET = @HAVE_WMEMSET@ HAVE_WS2TCPIP_H = @HAVE_WS2TCPIP_H@ HAVE_XLOCALE_H = @HAVE_XLOCALE_H@ HAVE__BOOL = @HAVE__BOOL@ HAVE__EXIT = @HAVE__EXIT@ INCLUDE_NEXT = @INCLUDE_NEXT@ INCLUDE_NEXT_AS_FIRST_DIRECTIVE = @INCLUDE_NEXT_AS_FIRST_DIRECTIVE@ INET_PTON_LIB = @INET_PTON_LIB@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INT32_MAX_LT_INTMAX_MAX = @INT32_MAX_LT_INTMAX_MAX@ INT64_MAX_EQ_LONG_MAX = @INT64_MAX_EQ_LONG_MAX@ INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LEXLIB = @LEXLIB@ LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ LIBAUGEAS_VERSION_INFO = @LIBAUGEAS_VERSION_INFO@ LIBFAILMALLOC = @LIBFAILMALLOC@ LIBFA_VERSION_INFO = @LIBFA_VERSION_INFO@ LIBINTL = @LIBINTL@ LIBMULTITHREAD = @LIBMULTITHREAD@ LIBOBJS = @LIBOBJS@ LIBPMULTITHREAD = @LIBPMULTITHREAD@ LIBPTHREAD = @LIBPTHREAD@ LIBS = @LIBS@ LIBSOCKET = @LIBSOCKET@ LIBSTDTHREAD = @LIBSTDTHREAD@ LIBTESTS_LIBDEPS = @LIBTESTS_LIBDEPS@ LIBTHREAD = @LIBTHREAD@ LIBTOOL = @LIBTOOL@ LIBXML_CFLAGS = @LIBXML_CFLAGS@ LIBXML_LIBS = @LIBXML_LIBS@ LIB_CLOCK_GETTIME = @LIB_CLOCK_GETTIME@ LIB_GETRANDOM = @LIB_GETRANDOM@ LIB_HARD_LOCALE = @LIB_HARD_LOCALE@ LIB_MBRTOWC = @LIB_MBRTOWC@ LIB_NANOSLEEP = @LIB_NANOSLEEP@ LIB_NL_LANGINFO = @LIB_NL_LANGINFO@ LIB_PTHREAD = @LIB_PTHREAD@ LIB_PTHREAD_SIGMASK = @LIB_PTHREAD_SIGMASK@ LIB_SCHED_YIELD = @LIB_SCHED_YIELD@ LIB_SELECT = @LIB_SELECT@ LIB_SELINUX = @LIB_SELINUX@ LIB_SEMAPHORE = @LIB_SEMAPHORE@ LIB_SETLOCALE = @LIB_SETLOCALE@ LIB_SETLOCALE_NULL = @LIB_SETLOCALE_NULL@ LIMITS_H = @LIMITS_H@ LIPO = @LIPO@ LN_S = @LN_S@ LOCALCHARSET_TESTS_ENVIRONMENT = @LOCALCHARSET_TESTS_ENVIRONMENT@ LOCALENAME_ENHANCE_LOCALE_FUNCS = @LOCALENAME_ENHANCE_LOCALE_FUNCS@ LOCALE_FR = @LOCALE_FR@ LOCALE_FR_UTF8 = @LOCALE_FR_UTF8@ LOCALE_JA = @LOCALE_JA@ LOCALE_TR_UTF8 = @LOCALE_TR_UTF8@ LOCALE_ZH_CN = @LOCALE_ZH_CN@ LTLIBINTL = @LTLIBINTL@ LTLIBMULTITHREAD = @LTLIBMULTITHREAD@ LTLIBOBJS = @LTLIBOBJS@ LTLIBTHREAD = @LTLIBTHREAD@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ ND_FORMAT = @ND_FORMAT@ ND_PROG = @ND_PROG@ NETINET_IN_H = @NETINET_IN_H@ NEXT_ARPA_INET_H = @NEXT_ARPA_INET_H@ NEXT_AS_FIRST_DIRECTIVE_ARPA_INET_H = @NEXT_AS_FIRST_DIRECTIVE_ARPA_INET_H@ NEXT_AS_FIRST_DIRECTIVE_CTYPE_H = @NEXT_AS_FIRST_DIRECTIVE_CTYPE_H@ NEXT_AS_FIRST_DIRECTIVE_ERRNO_H = @NEXT_AS_FIRST_DIRECTIVE_ERRNO_H@ NEXT_AS_FIRST_DIRECTIVE_FCNTL_H = @NEXT_AS_FIRST_DIRECTIVE_FCNTL_H@ NEXT_AS_FIRST_DIRECTIVE_FLOAT_H = @NEXT_AS_FIRST_DIRECTIVE_FLOAT_H@ NEXT_AS_FIRST_DIRECTIVE_FNMATCH_H = @NEXT_AS_FIRST_DIRECTIVE_FNMATCH_H@ NEXT_AS_FIRST_DIRECTIVE_GETOPT_H = @NEXT_AS_FIRST_DIRECTIVE_GETOPT_H@ NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H = @NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H@ NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H = @NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H@ NEXT_AS_FIRST_DIRECTIVE_LIMITS_H = @NEXT_AS_FIRST_DIRECTIVE_LIMITS_H@ NEXT_AS_FIRST_DIRECTIVE_LOCALE_H = @NEXT_AS_FIRST_DIRECTIVE_LOCALE_H@ NEXT_AS_FIRST_DIRECTIVE_NETINET_IN_H = @NEXT_AS_FIRST_DIRECTIVE_NETINET_IN_H@ NEXT_AS_FIRST_DIRECTIVE_PTHREAD_H = @NEXT_AS_FIRST_DIRECTIVE_PTHREAD_H@ NEXT_AS_FIRST_DIRECTIVE_SCHED_H = @NEXT_AS_FIRST_DIRECTIVE_SCHED_H@ NEXT_AS_FIRST_DIRECTIVE_SELINUX_SELINUX_H = @NEXT_AS_FIRST_DIRECTIVE_SELINUX_SELINUX_H@ NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H = @NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H@ NEXT_AS_FIRST_DIRECTIVE_STDDEF_H = @NEXT_AS_FIRST_DIRECTIVE_STDDEF_H@ NEXT_AS_FIRST_DIRECTIVE_STDINT_H = @NEXT_AS_FIRST_DIRECTIVE_STDINT_H@ NEXT_AS_FIRST_DIRECTIVE_STDIO_H = @NEXT_AS_FIRST_DIRECTIVE_STDIO_H@ NEXT_AS_FIRST_DIRECTIVE_STDLIB_H = @NEXT_AS_FIRST_DIRECTIVE_STDLIB_H@ NEXT_AS_FIRST_DIRECTIVE_STRING_H = @NEXT_AS_FIRST_DIRECTIVE_STRING_H@ NEXT_AS_FIRST_DIRECTIVE_SYS_IOCTL_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_IOCTL_H@ NEXT_AS_FIRST_DIRECTIVE_SYS_RANDOM_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_RANDOM_H@ NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H@ NEXT_AS_FIRST_DIRECTIVE_SYS_SOCKET_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_SOCKET_H@ NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H@ NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H@ NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H@ NEXT_AS_FIRST_DIRECTIVE_SYS_UIO_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_UIO_H@ NEXT_AS_FIRST_DIRECTIVE_SYS_WAIT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_WAIT_H@ NEXT_AS_FIRST_DIRECTIVE_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_TIME_H@ NEXT_AS_FIRST_DIRECTIVE_UNISTD_H = @NEXT_AS_FIRST_DIRECTIVE_UNISTD_H@ NEXT_AS_FIRST_DIRECTIVE_WCHAR_H = @NEXT_AS_FIRST_DIRECTIVE_WCHAR_H@ NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H = @NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H@ NEXT_CTYPE_H = @NEXT_CTYPE_H@ NEXT_ERRNO_H = @NEXT_ERRNO_H@ NEXT_FCNTL_H = @NEXT_FCNTL_H@ NEXT_FLOAT_H = @NEXT_FLOAT_H@ NEXT_FNMATCH_H = @NEXT_FNMATCH_H@ NEXT_GETOPT_H = @NEXT_GETOPT_H@ NEXT_INTTYPES_H = @NEXT_INTTYPES_H@ NEXT_LANGINFO_H = @NEXT_LANGINFO_H@ NEXT_LIMITS_H = @NEXT_LIMITS_H@ NEXT_LOCALE_H = @NEXT_LOCALE_H@ NEXT_NETINET_IN_H = @NEXT_NETINET_IN_H@ NEXT_PTHREAD_H = @NEXT_PTHREAD_H@ NEXT_SCHED_H = @NEXT_SCHED_H@ NEXT_SELINUX_SELINUX_H = @NEXT_SELINUX_SELINUX_H@ NEXT_SIGNAL_H = @NEXT_SIGNAL_H@ NEXT_STDDEF_H = @NEXT_STDDEF_H@ NEXT_STDINT_H = @NEXT_STDINT_H@ NEXT_STDIO_H = @NEXT_STDIO_H@ NEXT_STDLIB_H = @NEXT_STDLIB_H@ NEXT_STRING_H = @NEXT_STRING_H@ NEXT_SYS_IOCTL_H = @NEXT_SYS_IOCTL_H@ NEXT_SYS_RANDOM_H = @NEXT_SYS_RANDOM_H@ NEXT_SYS_SELECT_H = @NEXT_SYS_SELECT_H@ NEXT_SYS_SOCKET_H = @NEXT_SYS_SOCKET_H@ NEXT_SYS_STAT_H = @NEXT_SYS_STAT_H@ NEXT_SYS_TIME_H = @NEXT_SYS_TIME_H@ NEXT_SYS_TYPES_H = @NEXT_SYS_TYPES_H@ NEXT_SYS_UIO_H = @NEXT_SYS_UIO_H@ NEXT_SYS_WAIT_H = @NEXT_SYS_WAIT_H@ NEXT_TIME_H = @NEXT_TIME_H@ NEXT_UNISTD_H = @NEXT_UNISTD_H@ NEXT_WCHAR_H = @NEXT_WCHAR_H@ NEXT_WCTYPE_H = @NEXT_WCTYPE_H@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PC_SELINUX = @PC_SELINUX@ PDFDOCS = @PDFDOCS@ PDFLATEX = @PDFLATEX@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PRAGMA_COLUMNS = @PRAGMA_COLUMNS@ PRAGMA_SYSTEM_HEADER = @PRAGMA_SYSTEM_HEADER@ PRIPTR_PREFIX = @PRIPTR_PREFIX@ PTHREAD_H_DEFINES_STRUCT_TIMESPEC = @PTHREAD_H_DEFINES_STRUCT_TIMESPEC@ PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@ RANLIB = @RANLIB@ READLINE_LIBS = @READLINE_LIBS@ REPLACE_ACCESS = @REPLACE_ACCESS@ REPLACE_ALIGNED_ALLOC = @REPLACE_ALIGNED_ALLOC@ REPLACE_BTOWC = @REPLACE_BTOWC@ REPLACE_CALLOC = @REPLACE_CALLOC@ REPLACE_CANONICALIZE_FILE_NAME = @REPLACE_CANONICALIZE_FILE_NAME@ REPLACE_CHOWN = @REPLACE_CHOWN@ REPLACE_CLOSE = @REPLACE_CLOSE@ REPLACE_CREAT = @REPLACE_CREAT@ REPLACE_CTIME = @REPLACE_CTIME@ REPLACE_DPRINTF = @REPLACE_DPRINTF@ REPLACE_DUP = @REPLACE_DUP@ REPLACE_DUP2 = @REPLACE_DUP2@ REPLACE_DUPLOCALE = @REPLACE_DUPLOCALE@ REPLACE_EXECL = @REPLACE_EXECL@ REPLACE_EXECLE = @REPLACE_EXECLE@ REPLACE_EXECLP = @REPLACE_EXECLP@ REPLACE_EXECV = @REPLACE_EXECV@ REPLACE_EXECVE = @REPLACE_EXECVE@ REPLACE_EXECVP = @REPLACE_EXECVP@ REPLACE_EXECVPE = @REPLACE_EXECVPE@ REPLACE_FACCESSAT = @REPLACE_FACCESSAT@ REPLACE_FCHMODAT = @REPLACE_FCHMODAT@ REPLACE_FCHOWNAT = @REPLACE_FCHOWNAT@ REPLACE_FCLOSE = @REPLACE_FCLOSE@ REPLACE_FCNTL = @REPLACE_FCNTL@ REPLACE_FDOPEN = @REPLACE_FDOPEN@ REPLACE_FFLUSH = @REPLACE_FFLUSH@ REPLACE_FFSLL = @REPLACE_FFSLL@ REPLACE_FNMATCH = @REPLACE_FNMATCH@ REPLACE_FOPEN = @REPLACE_FOPEN@ REPLACE_FPRINTF = @REPLACE_FPRINTF@ REPLACE_FPURGE = @REPLACE_FPURGE@ REPLACE_FREE = @REPLACE_FREE@ REPLACE_FREELOCALE = @REPLACE_FREELOCALE@ REPLACE_FREOPEN = @REPLACE_FREOPEN@ REPLACE_FSEEK = @REPLACE_FSEEK@ REPLACE_FSEEKO = @REPLACE_FSEEKO@ REPLACE_FSTAT = @REPLACE_FSTAT@ REPLACE_FSTATAT = @REPLACE_FSTATAT@ REPLACE_FTELL = @REPLACE_FTELL@ REPLACE_FTELLO = @REPLACE_FTELLO@ REPLACE_FTRUNCATE = @REPLACE_FTRUNCATE@ REPLACE_FUTIMENS = @REPLACE_FUTIMENS@ REPLACE_GETCWD = @REPLACE_GETCWD@ REPLACE_GETDELIM = @REPLACE_GETDELIM@ REPLACE_GETDOMAINNAME = @REPLACE_GETDOMAINNAME@ REPLACE_GETDTABLESIZE = @REPLACE_GETDTABLESIZE@ REPLACE_GETGROUPS = @REPLACE_GETGROUPS@ REPLACE_GETLINE = @REPLACE_GETLINE@ REPLACE_GETLOGIN_R = @REPLACE_GETLOGIN_R@ REPLACE_GETPAGESIZE = @REPLACE_GETPAGESIZE@ REPLACE_GETPASS = @REPLACE_GETPASS@ REPLACE_GETRANDOM = @REPLACE_GETRANDOM@ REPLACE_GETTIMEOFDAY = @REPLACE_GETTIMEOFDAY@ REPLACE_GMTIME = @REPLACE_GMTIME@ REPLACE_INET_NTOP = @REPLACE_INET_NTOP@ REPLACE_INET_PTON = @REPLACE_INET_PTON@ REPLACE_INITSTATE = @REPLACE_INITSTATE@ REPLACE_IOCTL = @REPLACE_IOCTL@ REPLACE_ISATTY = @REPLACE_ISATTY@ REPLACE_ISWBLANK = @REPLACE_ISWBLANK@ REPLACE_ISWCNTRL = @REPLACE_ISWCNTRL@ REPLACE_ISWDIGIT = @REPLACE_ISWDIGIT@ REPLACE_ISWXDIGIT = @REPLACE_ISWXDIGIT@ REPLACE_ITOLD = @REPLACE_ITOLD@ REPLACE_LCHOWN = @REPLACE_LCHOWN@ REPLACE_LINK = @REPLACE_LINK@ REPLACE_LINKAT = @REPLACE_LINKAT@ REPLACE_LOCALECONV = @REPLACE_LOCALECONV@ REPLACE_LOCALTIME = @REPLACE_LOCALTIME@ REPLACE_LOCALTIME_R = @REPLACE_LOCALTIME_R@ REPLACE_LSEEK = @REPLACE_LSEEK@ REPLACE_LSTAT = @REPLACE_LSTAT@ REPLACE_MALLOC = @REPLACE_MALLOC@ REPLACE_MBRLEN = @REPLACE_MBRLEN@ REPLACE_MBRTOWC = @REPLACE_MBRTOWC@ REPLACE_MBSINIT = @REPLACE_MBSINIT@ REPLACE_MBSNRTOWCS = @REPLACE_MBSNRTOWCS@ REPLACE_MBSRTOWCS = @REPLACE_MBSRTOWCS@ REPLACE_MBSTATE_T = @REPLACE_MBSTATE_T@ REPLACE_MBTOWC = @REPLACE_MBTOWC@ REPLACE_MEMCHR = @REPLACE_MEMCHR@ REPLACE_MEMMEM = @REPLACE_MEMMEM@ REPLACE_MKDIR = @REPLACE_MKDIR@ REPLACE_MKFIFO = @REPLACE_MKFIFO@ REPLACE_MKFIFOAT = @REPLACE_MKFIFOAT@ REPLACE_MKNOD = @REPLACE_MKNOD@ REPLACE_MKNODAT = @REPLACE_MKNODAT@ REPLACE_MKSTEMP = @REPLACE_MKSTEMP@ REPLACE_MKTIME = @REPLACE_MKTIME@ REPLACE_NANOSLEEP = @REPLACE_NANOSLEEP@ REPLACE_NEWLOCALE = @REPLACE_NEWLOCALE@ REPLACE_NL_LANGINFO = @REPLACE_NL_LANGINFO@ REPLACE_NULL = @REPLACE_NULL@ REPLACE_OBSTACK_PRINTF = @REPLACE_OBSTACK_PRINTF@ REPLACE_OPEN = @REPLACE_OPEN@ REPLACE_OPENAT = @REPLACE_OPENAT@ REPLACE_PERROR = @REPLACE_PERROR@ REPLACE_POPEN = @REPLACE_POPEN@ REPLACE_POSIX_MEMALIGN = @REPLACE_POSIX_MEMALIGN@ REPLACE_PREAD = @REPLACE_PREAD@ REPLACE_PRINTF = @REPLACE_PRINTF@ REPLACE_PSELECT = @REPLACE_PSELECT@ REPLACE_PTHREAD_ATTR_DESTROY = @REPLACE_PTHREAD_ATTR_DESTROY@ REPLACE_PTHREAD_ATTR_GETDETACHSTATE = @REPLACE_PTHREAD_ATTR_GETDETACHSTATE@ REPLACE_PTHREAD_ATTR_INIT = @REPLACE_PTHREAD_ATTR_INIT@ REPLACE_PTHREAD_ATTR_SETDETACHSTATE = @REPLACE_PTHREAD_ATTR_SETDETACHSTATE@ REPLACE_PTHREAD_CONDATTR_DESTROY = @REPLACE_PTHREAD_CONDATTR_DESTROY@ REPLACE_PTHREAD_CONDATTR_INIT = @REPLACE_PTHREAD_CONDATTR_INIT@ REPLACE_PTHREAD_COND_BROADCAST = @REPLACE_PTHREAD_COND_BROADCAST@ REPLACE_PTHREAD_COND_DESTROY = @REPLACE_PTHREAD_COND_DESTROY@ REPLACE_PTHREAD_COND_INIT = @REPLACE_PTHREAD_COND_INIT@ REPLACE_PTHREAD_COND_SIGNAL = @REPLACE_PTHREAD_COND_SIGNAL@ REPLACE_PTHREAD_COND_TIMEDWAIT = @REPLACE_PTHREAD_COND_TIMEDWAIT@ REPLACE_PTHREAD_COND_WAIT = @REPLACE_PTHREAD_COND_WAIT@ REPLACE_PTHREAD_CREATE = @REPLACE_PTHREAD_CREATE@ REPLACE_PTHREAD_DETACH = @REPLACE_PTHREAD_DETACH@ REPLACE_PTHREAD_EQUAL = @REPLACE_PTHREAD_EQUAL@ REPLACE_PTHREAD_EXIT = @REPLACE_PTHREAD_EXIT@ REPLACE_PTHREAD_GETSPECIFIC = @REPLACE_PTHREAD_GETSPECIFIC@ REPLACE_PTHREAD_JOIN = @REPLACE_PTHREAD_JOIN@ REPLACE_PTHREAD_KEY_CREATE = @REPLACE_PTHREAD_KEY_CREATE@ REPLACE_PTHREAD_KEY_DELETE = @REPLACE_PTHREAD_KEY_DELETE@ REPLACE_PTHREAD_MUTEXATTR_DESTROY = @REPLACE_PTHREAD_MUTEXATTR_DESTROY@ REPLACE_PTHREAD_MUTEXATTR_GETROBUST = @REPLACE_PTHREAD_MUTEXATTR_GETROBUST@ REPLACE_PTHREAD_MUTEXATTR_GETTYPE = @REPLACE_PTHREAD_MUTEXATTR_GETTYPE@ REPLACE_PTHREAD_MUTEXATTR_INIT = @REPLACE_PTHREAD_MUTEXATTR_INIT@ REPLACE_PTHREAD_MUTEXATTR_SETROBUST = @REPLACE_PTHREAD_MUTEXATTR_SETROBUST@ REPLACE_PTHREAD_MUTEXATTR_SETTYPE = @REPLACE_PTHREAD_MUTEXATTR_SETTYPE@ REPLACE_PTHREAD_MUTEX_DESTROY = @REPLACE_PTHREAD_MUTEX_DESTROY@ REPLACE_PTHREAD_MUTEX_INIT = @REPLACE_PTHREAD_MUTEX_INIT@ REPLACE_PTHREAD_MUTEX_LOCK = @REPLACE_PTHREAD_MUTEX_LOCK@ REPLACE_PTHREAD_MUTEX_TIMEDLOCK = @REPLACE_PTHREAD_MUTEX_TIMEDLOCK@ REPLACE_PTHREAD_MUTEX_TRYLOCK = @REPLACE_PTHREAD_MUTEX_TRYLOCK@ REPLACE_PTHREAD_MUTEX_UNLOCK = @REPLACE_PTHREAD_MUTEX_UNLOCK@ REPLACE_PTHREAD_ONCE = @REPLACE_PTHREAD_ONCE@ REPLACE_PTHREAD_RWLOCKATTR_DESTROY = @REPLACE_PTHREAD_RWLOCKATTR_DESTROY@ REPLACE_PTHREAD_RWLOCKATTR_INIT = @REPLACE_PTHREAD_RWLOCKATTR_INIT@ REPLACE_PTHREAD_RWLOCK_DESTROY = @REPLACE_PTHREAD_RWLOCK_DESTROY@ REPLACE_PTHREAD_RWLOCK_INIT = @REPLACE_PTHREAD_RWLOCK_INIT@ REPLACE_PTHREAD_RWLOCK_RDLOCK = @REPLACE_PTHREAD_RWLOCK_RDLOCK@ REPLACE_PTHREAD_RWLOCK_TIMEDRDLOCK = @REPLACE_PTHREAD_RWLOCK_TIMEDRDLOCK@ REPLACE_PTHREAD_RWLOCK_TIMEDWRLOCK = @REPLACE_PTHREAD_RWLOCK_TIMEDWRLOCK@ REPLACE_PTHREAD_RWLOCK_TRYRDLOCK = @REPLACE_PTHREAD_RWLOCK_TRYRDLOCK@ REPLACE_PTHREAD_RWLOCK_TRYWRLOCK = @REPLACE_PTHREAD_RWLOCK_TRYWRLOCK@ REPLACE_PTHREAD_RWLOCK_UNLOCK = @REPLACE_PTHREAD_RWLOCK_UNLOCK@ REPLACE_PTHREAD_RWLOCK_WRLOCK = @REPLACE_PTHREAD_RWLOCK_WRLOCK@ REPLACE_PTHREAD_SELF = @REPLACE_PTHREAD_SELF@ REPLACE_PTHREAD_SETSPECIFIC = @REPLACE_PTHREAD_SETSPECIFIC@ REPLACE_PTHREAD_SIGMASK = @REPLACE_PTHREAD_SIGMASK@ REPLACE_PTHREAD_SPIN_DESTROY = @REPLACE_PTHREAD_SPIN_DESTROY@ REPLACE_PTHREAD_SPIN_INIT = @REPLACE_PTHREAD_SPIN_INIT@ REPLACE_PTHREAD_SPIN_LOCK = @REPLACE_PTHREAD_SPIN_LOCK@ REPLACE_PTHREAD_SPIN_TRYLOCK = @REPLACE_PTHREAD_SPIN_TRYLOCK@ REPLACE_PTHREAD_SPIN_UNLOCK = @REPLACE_PTHREAD_SPIN_UNLOCK@ REPLACE_PTSNAME = @REPLACE_PTSNAME@ REPLACE_PTSNAME_R = @REPLACE_PTSNAME_R@ REPLACE_PUTENV = @REPLACE_PUTENV@ REPLACE_PWRITE = @REPLACE_PWRITE@ REPLACE_QSORT_R = @REPLACE_QSORT_R@ REPLACE_RAISE = @REPLACE_RAISE@ REPLACE_RANDOM = @REPLACE_RANDOM@ REPLACE_RANDOM_R = @REPLACE_RANDOM_R@ REPLACE_READ = @REPLACE_READ@ REPLACE_READLINK = @REPLACE_READLINK@ REPLACE_READLINKAT = @REPLACE_READLINKAT@ REPLACE_REALLOC = @REPLACE_REALLOC@ REPLACE_REALPATH = @REPLACE_REALPATH@ REPLACE_REMOVE = @REPLACE_REMOVE@ REPLACE_RENAME = @REPLACE_RENAME@ REPLACE_RENAMEAT = @REPLACE_RENAMEAT@ REPLACE_RMDIR = @REPLACE_RMDIR@ REPLACE_SCHED_YIELD = @REPLACE_SCHED_YIELD@ REPLACE_SELECT = @REPLACE_SELECT@ REPLACE_SETENV = @REPLACE_SETENV@ REPLACE_SETLOCALE = @REPLACE_SETLOCALE@ REPLACE_SETSTATE = @REPLACE_SETSTATE@ REPLACE_SLEEP = @REPLACE_SLEEP@ REPLACE_SNPRINTF = @REPLACE_SNPRINTF@ REPLACE_SPRINTF = @REPLACE_SPRINTF@ REPLACE_STAT = @REPLACE_STAT@ REPLACE_STDIO_READ_FUNCS = @REPLACE_STDIO_READ_FUNCS@ REPLACE_STDIO_WRITE_FUNCS = @REPLACE_STDIO_WRITE_FUNCS@ REPLACE_STPNCPY = @REPLACE_STPNCPY@ REPLACE_STRCASESTR = @REPLACE_STRCASESTR@ REPLACE_STRCHRNUL = @REPLACE_STRCHRNUL@ REPLACE_STRDUP = @REPLACE_STRDUP@ REPLACE_STRERROR = @REPLACE_STRERROR@ REPLACE_STRERRORNAME_NP = @REPLACE_STRERRORNAME_NP@ REPLACE_STRERROR_R = @REPLACE_STRERROR_R@ REPLACE_STRFTIME = @REPLACE_STRFTIME@ REPLACE_STRNCAT = @REPLACE_STRNCAT@ REPLACE_STRNDUP = @REPLACE_STRNDUP@ REPLACE_STRNLEN = @REPLACE_STRNLEN@ REPLACE_STRSIGNAL = @REPLACE_STRSIGNAL@ REPLACE_STRSTR = @REPLACE_STRSTR@ REPLACE_STRTOD = @REPLACE_STRTOD@ REPLACE_STRTOIMAX = @REPLACE_STRTOIMAX@ REPLACE_STRTOK_R = @REPLACE_STRTOK_R@ REPLACE_STRTOLD = @REPLACE_STRTOLD@ REPLACE_STRTOUMAX = @REPLACE_STRTOUMAX@ REPLACE_STRUCT_LCONV = @REPLACE_STRUCT_LCONV@ REPLACE_STRUCT_TIMEVAL = @REPLACE_STRUCT_TIMEVAL@ REPLACE_SYMLINK = @REPLACE_SYMLINK@ REPLACE_SYMLINKAT = @REPLACE_SYMLINKAT@ REPLACE_TIMEGM = @REPLACE_TIMEGM@ REPLACE_TMPFILE = @REPLACE_TMPFILE@ REPLACE_TOWLOWER = @REPLACE_TOWLOWER@ REPLACE_TRUNCATE = @REPLACE_TRUNCATE@ REPLACE_TTYNAME_R = @REPLACE_TTYNAME_R@ REPLACE_TZSET = @REPLACE_TZSET@ REPLACE_UNLINK = @REPLACE_UNLINK@ REPLACE_UNLINKAT = @REPLACE_UNLINKAT@ REPLACE_UNSETENV = @REPLACE_UNSETENV@ REPLACE_USLEEP = @REPLACE_USLEEP@ REPLACE_UTIMENSAT = @REPLACE_UTIMENSAT@ REPLACE_VASPRINTF = @REPLACE_VASPRINTF@ REPLACE_VDPRINTF = @REPLACE_VDPRINTF@ REPLACE_VFPRINTF = @REPLACE_VFPRINTF@ REPLACE_VPRINTF = @REPLACE_VPRINTF@ REPLACE_VSNPRINTF = @REPLACE_VSNPRINTF@ REPLACE_VSPRINTF = @REPLACE_VSPRINTF@ REPLACE_WCRTOMB = @REPLACE_WCRTOMB@ REPLACE_WCSFTIME = @REPLACE_WCSFTIME@ REPLACE_WCSNRTOMBS = @REPLACE_WCSNRTOMBS@ REPLACE_WCSRTOMBS = @REPLACE_WCSRTOMBS@ REPLACE_WCSTOK = @REPLACE_WCSTOK@ REPLACE_WCSWIDTH = @REPLACE_WCSWIDTH@ REPLACE_WCTOB = @REPLACE_WCTOB@ REPLACE_WCTOMB = @REPLACE_WCTOMB@ REPLACE_WCWIDTH = @REPLACE_WCWIDTH@ REPLACE_WRITE = @REPLACE_WRITE@ SED = @SED@ SELINUX_CONTEXT_H = @SELINUX_CONTEXT_H@ SELINUX_LABEL_H = @SELINUX_LABEL_H@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@ SIZE_T_SUFFIX = @SIZE_T_SUFFIX@ STDALIGN_H = @STDALIGN_H@ STDBOOL_H = @STDBOOL_H@ STDDEF_H = @STDDEF_H@ STDINT_H = @STDINT_H@ STRIP = @STRIP@ SYS_IOCTL_H_HAVE_WINSOCK2_H = @SYS_IOCTL_H_HAVE_WINSOCK2_H@ SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@ SYS_TIME_H_DEFINES_STRUCT_TIMESPEC = @SYS_TIME_H_DEFINES_STRUCT_TIMESPEC@ TIME_H_DEFINES_STRUCT_TIMESPEC = @TIME_H_DEFINES_STRUCT_TIMESPEC@ TIME_H_DEFINES_TIME_UTC = @TIME_H_DEFINES_TIME_UTC@ UINT32_MAX_LT_UINTMAX_MAX = @UINT32_MAX_LT_UINTMAX_MAX@ UINT64_MAX_EQ_ULONG_MAX = @UINT64_MAX_EQ_ULONG_MAX@ UNDEFINE_STRTOK_R = @UNDEFINE_STRTOK_R@ UNISTD_H_DEFINES_STRUCT_TIMESPEC = @UNISTD_H_DEFINES_STRUCT_TIMESPEC@ UNISTD_H_HAVE_SYS_RANDOM_H = @UNISTD_H_HAVE_SYS_RANDOM_H@ UNISTD_H_HAVE_WINSOCK2_H = @UNISTD_H_HAVE_WINSOCK2_H@ UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@ VERSION = @VERSION@ VERSION_SCRIPT_FLAGS = @VERSION_SCRIPT_FLAGS@ WARN_CFLAGS = @WARN_CFLAGS@ WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@ WINDOWS_64_BIT_OFF_T = @WINDOWS_64_BIT_OFF_T@ WINDOWS_64_BIT_ST_SIZE = @WINDOWS_64_BIT_ST_SIZE@ WINDOWS_STAT_INODES = @WINDOWS_STAT_INODES@ WINDOWS_STAT_TIMESPEC = @WINDOWS_STAT_TIMESPEC@ WINT_T_SUFFIX = @WINT_T_SUFFIX@ YACC = @YACC@ YFLAGS = @YFLAGS@ YIELD_LIB = @YIELD_LIB@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ gl_LIBOBJS = @gl_LIBOBJS@ gl_LTLIBOBJS = @gl_LTLIBOBJS@ gltests_LIBOBJS = @gltests_LIBOBJS@ gltests_LTLIBOBJS = @gltests_LTLIBOBJS@ gltests_WITNESS = @gltests_WITNESS@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ GNULIB = ../gnulib/lib/libgnu.la GNULIB_CFLAGS = -I $(top_builddir)/gnulib/lib -I $(top_srcdir)/gnulib/lib AM_CFLAGS = @AUGEAS_CFLAGS@ @WARN_CFLAGS@ $(GNULIB_CFLAGS) $(LIBXML_CFLAGS) AM_YFLAGS = -d -p spec_ EXTRA_DIST = try augeas_sym.version fa_sym.version BUILT_SOURCES = datadir.h DISTCLEANFILES = datadir.h lib_LTLIBRARIES = libfa.la libaugeas.la noinst_LTLIBRARIES = liblexer.la include_HEADERS = augeas.h fa.h libaugeas_la_SOURCES = augeas.h augeas.c augrun.c pathx.c \ internal.h internal.c \ memory.h memory.c ref.h ref.c \ syntax.c syntax.h parser.y builtin.c lens.c lens.h regexp.c regexp.h \ transform.h transform.c ast.c get.c put.c list.h \ info.c info.h errcode.c errcode.h jmt.h jmt.c xml.c @USE_VERSION_SCRIPT_FALSE@AUGEAS_VERSION_SCRIPT = @USE_VERSION_SCRIPT_TRUE@AUGEAS_VERSION_SCRIPT = $(VERSION_SCRIPT_FLAGS)$(srcdir)/augeas_sym.version @USE_VERSION_SCRIPT_FALSE@FA_VERSION_SCRIPT = @USE_VERSION_SCRIPT_TRUE@FA_VERSION_SCRIPT = $(VERSION_SCRIPT_FLAGS)$(srcdir)/fa_sym.version libaugeas_la_LDFLAGS = $(AUGEAS_VERSION_SCRIPT) \ -version-info $(LIBAUGEAS_VERSION_INFO) libaugeas_la_LIBADD = liblexer.la libfa.la $(LIB_SELINUX) $(LIBXML_LIBS) $(GNULIB) augtool_SOURCES = augtool.c augtool_LDADD = libaugeas.la $(READLINE_LIBS) $(LIBXML_LIBS) $(GNULIB) augparse_SOURCES = augparse.c augparse_LDADD = libaugeas.la $(LIBXML_LIBS) $(GNULIB) augmatch_SOURCES = augmatch.c augmatch_LDADD = libaugeas.la $(LIBXML_LIBS) $(GNULIB) libfa_la_SOURCES = fa.c fa.h hash.c hash.h memory.c memory.h ref.h ref.c libfa_la_LIBADD = $(LIB_SELINUX) $(GNULIB) libfa_la_LDFLAGS = $(FA_VERSION_SCRIPT) -version-info $(LIBFA_VERSION_INFO) liblexer_la_SOURCES = lexer.l liblexer_la_CFLAGS = $(AM_CFLAGS) -Wno-error all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .c .l .lo .o .obj .y $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/Makefile.inc $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_srcdir)/Makefile.inc $(am__empty): $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } parser.h: parser.c @if test ! -f $@; then rm -f parser.c; else :; fi @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) parser.c; else :; fi libaugeas.la: $(libaugeas_la_OBJECTS) $(libaugeas_la_DEPENDENCIES) $(EXTRA_libaugeas_la_DEPENDENCIES) $(AM_V_CCLD)$(libaugeas_la_LINK) -rpath $(libdir) $(libaugeas_la_OBJECTS) $(libaugeas_la_LIBADD) $(LIBS) libfa.la: $(libfa_la_OBJECTS) $(libfa_la_DEPENDENCIES) $(EXTRA_libfa_la_DEPENDENCIES) $(AM_V_CCLD)$(libfa_la_LINK) -rpath $(libdir) $(libfa_la_OBJECTS) $(libfa_la_LIBADD) $(LIBS) liblexer.la: $(liblexer_la_OBJECTS) $(liblexer_la_DEPENDENCIES) $(EXTRA_liblexer_la_DEPENDENCIES) $(AM_V_CCLD)$(liblexer_la_LINK) $(liblexer_la_OBJECTS) $(liblexer_la_LIBADD) $(LIBS) augmatch$(EXEEXT): $(augmatch_OBJECTS) $(augmatch_DEPENDENCIES) $(EXTRA_augmatch_DEPENDENCIES) @rm -f augmatch$(EXEEXT) $(AM_V_CCLD)$(LINK) $(augmatch_OBJECTS) $(augmatch_LDADD) $(LIBS) augparse$(EXEEXT): $(augparse_OBJECTS) $(augparse_DEPENDENCIES) $(EXTRA_augparse_DEPENDENCIES) @rm -f augparse$(EXEEXT) $(AM_V_CCLD)$(LINK) $(augparse_OBJECTS) $(augparse_LDADD) $(LIBS) augtool$(EXEEXT): $(augtool_OBJECTS) $(augtool_DEPENDENCIES) $(EXTRA_augtool_DEPENDENCIES) @rm -f augtool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(augtool_OBJECTS) $(augtool_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ast.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/augeas.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/augmatch.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/augparse.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/augrun.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/augtool.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/builtin.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errcode.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fa.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/internal.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jmt.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lens.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblexer_la-lexer.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memory.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parser.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pathx.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/put.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ref.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regexp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/syntax.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/transform.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xml.Plo@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< liblexer_la-lexer.lo: lexer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblexer_la_CFLAGS) $(CFLAGS) -MT liblexer_la-lexer.lo -MD -MP -MF $(DEPDIR)/liblexer_la-lexer.Tpo -c -o liblexer_la-lexer.lo `test -f 'lexer.c' || echo '$(srcdir)/'`lexer.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblexer_la-lexer.Tpo $(DEPDIR)/liblexer_la-lexer.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lexer.c' object='liblexer_la-lexer.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblexer_la_CFLAGS) $(CFLAGS) -c -o liblexer_la-lexer.lo `test -f 'lexer.c' || echo '$(srcdir)/'`lexer.c .l.c: $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE) .y.c: $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE) mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-includeHEADERS: $(include_HEADERS) @$(NORMAL_INSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ done uninstall-includeHEADERS: @$(NORMAL_UNINSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-am all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(HEADERS) install-binPROGRAMS: install-libLTLIBRARIES installdirs: for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-am install-exec: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -rm -f lexer.c -rm -f parser.c -rm -f parser.h -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ clean-libtool clean-noinstLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -f ./$(DEPDIR)/ast.Plo -rm -f ./$(DEPDIR)/augeas.Plo -rm -f ./$(DEPDIR)/augmatch.Po -rm -f ./$(DEPDIR)/augparse.Po -rm -f ./$(DEPDIR)/augrun.Plo -rm -f ./$(DEPDIR)/augtool.Po -rm -f ./$(DEPDIR)/builtin.Plo -rm -f ./$(DEPDIR)/errcode.Plo -rm -f ./$(DEPDIR)/fa.Plo -rm -f ./$(DEPDIR)/get.Plo -rm -f ./$(DEPDIR)/hash.Plo -rm -f ./$(DEPDIR)/info.Plo -rm -f ./$(DEPDIR)/internal.Plo -rm -f ./$(DEPDIR)/jmt.Plo -rm -f ./$(DEPDIR)/lens.Plo -rm -f ./$(DEPDIR)/liblexer_la-lexer.Plo -rm -f ./$(DEPDIR)/memory.Plo -rm -f ./$(DEPDIR)/parser.Plo -rm -f ./$(DEPDIR)/pathx.Plo -rm -f ./$(DEPDIR)/put.Plo -rm -f ./$(DEPDIR)/ref.Plo -rm -f ./$(DEPDIR)/regexp.Plo -rm -f ./$(DEPDIR)/syntax.Plo -rm -f ./$(DEPDIR)/transform.Plo -rm -f ./$(DEPDIR)/xml.Plo -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-includeHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-libLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/ast.Plo -rm -f ./$(DEPDIR)/augeas.Plo -rm -f ./$(DEPDIR)/augmatch.Po -rm -f ./$(DEPDIR)/augparse.Po -rm -f ./$(DEPDIR)/augrun.Plo -rm -f ./$(DEPDIR)/augtool.Po -rm -f ./$(DEPDIR)/builtin.Plo -rm -f ./$(DEPDIR)/errcode.Plo -rm -f ./$(DEPDIR)/fa.Plo -rm -f ./$(DEPDIR)/get.Plo -rm -f ./$(DEPDIR)/hash.Plo -rm -f ./$(DEPDIR)/info.Plo -rm -f ./$(DEPDIR)/internal.Plo -rm -f ./$(DEPDIR)/jmt.Plo -rm -f ./$(DEPDIR)/lens.Plo -rm -f ./$(DEPDIR)/liblexer_la-lexer.Plo -rm -f ./$(DEPDIR)/memory.Plo -rm -f ./$(DEPDIR)/parser.Plo -rm -f ./$(DEPDIR)/pathx.Plo -rm -f ./$(DEPDIR)/put.Plo -rm -f ./$(DEPDIR)/ref.Plo -rm -f ./$(DEPDIR)/regexp.Plo -rm -f ./$(DEPDIR)/syntax.Plo -rm -f ./$(DEPDIR)/transform.Plo -rm -f ./$(DEPDIR)/xml.Plo -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \ uninstall-libLTLIBRARIES .MAKE: all check install install-am install-exec install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-binPROGRAMS \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-includeHEADERS install-info install-info-am \ install-libLTLIBRARIES install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \ uninstall-includeHEADERS uninstall-libLTLIBRARIES .PRECIOUS: Makefile FAILMALLOC_START ?= 1 FAILMALLOC_REP ?= 20 FAILMALLOC_PROG ?= ./augtool @WITH_FAILMALLOC_TRUE@failmalloc: failmalloc-run @WITH_FAILMALLOC_FALSE@failmalloc: failmalloc-error failmalloc-run: $(FAILMALLOC_PROG) @(echo "Running $(FAILMALLOC_PROG) with failmalloc"; \ for i in $$(seq $(FAILMALLOC_START) $$(expr $(FAILMALLOC_START) + $(FAILMALLOC_REP) - 1)) ; do \ resp=$$(libtool --mode=execute env LD_PRELOAD=$(LIBFAILMALLOC) FAILMALLOC_INTERVAL=$$i $(FAILMALLOC_PROG)); \ status=$$?; \ if [ $$status -ne 0 -a $$status -ne 2 ] ; then \ printf "%5d FAIL %3d %s\n" $$i $$status "$$resp" ; \ elif [ x$(V) = x1 -o $$(( $$i % 100 )) -eq 0 ] ; then \ printf "%5d PASS %s\n" $$i "$$resp" ; \ fi \ done) failmalloc-error: @(echo "You need to turn on failmalloc support with --with-failmalloc"; \ exit 1) # Generate datadir.h. AUGEAS_LENS_DIR in internal.h depends on # the value of DATADIR internal.h: datadir.h FORCE-datadir.h: Makefile echo '#define DATADIR "$(datadir)"' > datadir.h1 $(top_srcdir)/build/ac-aux/move-if-change datadir.h1 datadir.h datadir.h: FORCE-datadir.h # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: augeas-1.13.0/src/Makefile.am0000644000175000017500000000406514161102026012603 00000000000000GNULIB= ../gnulib/lib/libgnu.la GNULIB_CFLAGS= -I $(top_builddir)/gnulib/lib -I $(top_srcdir)/gnulib/lib AM_CFLAGS = @AUGEAS_CFLAGS@ @WARN_CFLAGS@ $(GNULIB_CFLAGS) $(LIBXML_CFLAGS) AM_YFLAGS=-d -p spec_ EXTRA_DIST = try augeas_sym.version fa_sym.version BUILT_SOURCES = datadir.h DISTCLEANFILES = datadir.h lib_LTLIBRARIES = libfa.la libaugeas.la noinst_LTLIBRARIES = liblexer.la bin_PROGRAMS = augtool augparse augmatch include_HEADERS = augeas.h fa.h libaugeas_la_SOURCES = augeas.h augeas.c augrun.c pathx.c \ internal.h internal.c \ memory.h memory.c ref.h ref.c \ syntax.c syntax.h parser.y builtin.c lens.c lens.h regexp.c regexp.h \ transform.h transform.c ast.c get.c put.c list.h \ info.c info.h errcode.c errcode.h jmt.h jmt.c xml.c if USE_VERSION_SCRIPT AUGEAS_VERSION_SCRIPT = $(VERSION_SCRIPT_FLAGS)$(srcdir)/augeas_sym.version FA_VERSION_SCRIPT = $(VERSION_SCRIPT_FLAGS)$(srcdir)/fa_sym.version else AUGEAS_VERSION_SCRIPT = FA_VERSION_SCRIPT = endif libaugeas_la_LDFLAGS = $(AUGEAS_VERSION_SCRIPT) \ -version-info $(LIBAUGEAS_VERSION_INFO) libaugeas_la_LIBADD = liblexer.la libfa.la $(LIB_SELINUX) $(LIBXML_LIBS) $(GNULIB) augtool_SOURCES = augtool.c augtool_LDADD = libaugeas.la $(READLINE_LIBS) $(LIBXML_LIBS) $(GNULIB) augparse_SOURCES = augparse.c augparse_LDADD = libaugeas.la $(LIBXML_LIBS) $(GNULIB) augmatch_SOURCES = augmatch.c augmatch_LDADD = libaugeas.la $(LIBXML_LIBS) $(GNULIB) libfa_la_SOURCES = fa.c fa.h hash.c hash.h memory.c memory.h ref.h ref.c libfa_la_LIBADD = $(LIB_SELINUX) $(GNULIB) libfa_la_LDFLAGS = $(FA_VERSION_SCRIPT) -version-info $(LIBFA_VERSION_INFO) liblexer_la_SOURCES = lexer.l liblexer_la_CFLAGS = $(AM_CFLAGS) -Wno-error FAILMALLOC_START ?= 1 FAILMALLOC_REP ?= 20 FAILMALLOC_PROG ?= ./augtool include $(top_srcdir)/Makefile.inc # Generate datadir.h. AUGEAS_LENS_DIR in internal.h depends on # the value of DATADIR internal.h: datadir.h FORCE-datadir.h: Makefile echo '#define DATADIR "$(datadir)"' > datadir.h1 $(top_srcdir)/build/ac-aux/move-if-change datadir.h1 datadir.h datadir.h: FORCE-datadir.h augeas-1.13.0/src/ref.h0000644000175000017500000000562714161102026011501 00000000000000/* * ref.h: reference counting macros * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #ifndef REF_H_ #define REF_H_ #include #include /* Reference counting for pointers to structs with a REF field of type ref_t * * When a pointer to such a struct is passed into a function that stores * it, the function can either "receive ownership", meaning it does not * increment the reference count, or it can "take ownership", meaning it * increments the reference count. In the first case, the reference is now * owned by wherever the function stored it, and not the caller anymore; in * the second case, the caller and whereever the reference was stored both * own the reference. */ // FIXME: This is not threadsafe; incr/decr ref needs to be protected #define REF_MAX UINT_MAX typedef unsigned int ref_t; int ref_make_ref(void *ptrptr, size_t size, size_t ref_ofs); #define make_ref(var) \ ref_make_ref(&(var), sizeof(*(var)), offsetof(typeof(*(var)), ref)) #define make_ref_err(var) if (make_ref(var) < 0) goto error #define ref(s) (((s) == NULL || (s)->ref == REF_MAX) ? (s) : ((s)->ref++, (s))) #define unref(s, t) \ do { \ if ((s) != NULL && (s)->ref != REF_MAX) { \ assert((s)->ref > 0); \ if (--(s)->ref == 0) { \ /*memset(s, 255, sizeof(*s));*/ \ free_##t(s); \ } \ } \ (s) = NULL; \ } while(0) /* Make VAR uncollectable and pin it in memory for eternity */ #define ref_pin(var) (var)->ref = REF_MAX #endif /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/xml.c0000644000175000017500000001211014161102026011501 00000000000000/* * xml.c: the implementation of aug_to_xml and supporting functions * * Copyright (C) 2017 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include "augeas.h" #include "internal.h" #include "memory.h" #include "info.h" #include "errcode.h" #include static int to_xml_span(xmlNodePtr elem, const char *pfor, int start, int end) { int r; char *buf; xmlAttrPtr prop; xmlNodePtr span_elem; span_elem = xmlNewChild(elem, NULL, BAD_CAST "span", NULL); if (span_elem == NULL) return -1; prop = xmlSetProp(span_elem, BAD_CAST "for", BAD_CAST pfor); if (prop == NULL) return -1; /* Format and set the start property */ r = xasprintf(&buf, "%d", start); if (r < 0) return -1; prop = xmlSetProp(span_elem, BAD_CAST "start", BAD_CAST buf); FREE(buf); if (prop == NULL) return -1; /* Format and set the end property */ r = xasprintf(&buf, "%d", end); if (r < 0) return -1; prop = xmlSetProp(span_elem, BAD_CAST "end", BAD_CAST buf); FREE(buf); if (prop == NULL) return -1; return 0; } static int to_xml_one(xmlNodePtr elem, const struct tree *tree, const char *pathin) { xmlNodePtr value; xmlAttrPtr prop; int r; prop = xmlSetProp(elem, BAD_CAST "label", BAD_CAST tree->label); if (prop == NULL) goto error; if (tree->span) { struct span *span = tree->span; prop = xmlSetProp(elem, BAD_CAST "file", BAD_CAST span->filename->str); if (prop == NULL) goto error; r = to_xml_span(elem, "label", span->label_start, span->label_end); if (r < 0) goto error; r = to_xml_span(elem, "value", span->value_start, span->value_end); if (r < 0) goto error; r = to_xml_span(elem, "node", span->span_start, span->span_end); if (r < 0) goto error; } if (pathin != NULL) { prop = xmlSetProp(elem, BAD_CAST "path", BAD_CAST pathin); if (prop == NULL) goto error; } if (tree->value != NULL) { value = xmlNewTextChild(elem, NULL, BAD_CAST "value", BAD_CAST tree->value); if (value == NULL) goto error; } return 0; error: return -1; } static int to_xml_rec(xmlNodePtr pnode, struct tree *start, const char *pathin) { int r; xmlNodePtr elem; elem = xmlNewChild(pnode, NULL, BAD_CAST "node", NULL); if (elem == NULL) goto error; r = to_xml_one(elem, start, pathin); if (r < 0) goto error; list_for_each(tree, start->children) { if (TREE_HIDDEN(tree)) continue; r = to_xml_rec(elem, tree, NULL); if (r < 0) goto error; } return 0; error: return -1; } static int tree_to_xml(struct pathx *p, xmlNode **xml, const char *pathin) { char *path = NULL; struct tree *tree; xmlAttrPtr expr; int r; *xml = xmlNewNode(NULL, BAD_CAST "augeas"); if (*xml == NULL) goto error; expr = xmlSetProp(*xml, BAD_CAST "match", BAD_CAST pathin); if (expr == NULL) goto error; for (tree = pathx_first(p); tree != NULL; tree = pathx_next(p)) { if (TREE_HIDDEN(tree)) continue; path = path_of_tree(tree); if (path == NULL) goto error; r = to_xml_rec(*xml, tree, path); if (r < 0) goto error; FREE(path); } return 0; error: free(path); xmlFree(*xml); *xml = NULL; return -1; } int aug_to_xml(const struct augeas *aug, const char *pathin, xmlNode **xmldoc, unsigned int flags) { struct pathx *p = NULL; int result = -1; api_entry(aug); ARG_CHECK(flags != 0, aug, "aug_to_xml: FLAGS must be 0"); ARG_CHECK(xmldoc == NULL, aug, "aug_to_xml: XMLDOC must be non-NULL"); *xmldoc = NULL; if (pathin == NULL || strlen(pathin) == 0 || strcmp(pathin, "/") == 0) { pathin = "/*"; } p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), pathin, true); ERR_BAIL(aug); result = tree_to_xml(p, xmldoc, pathin); ERR_THROW(result < 0, aug, AUG_ENOMEM, NULL); error: free_pathx(p); api_exit(aug); return result; } augeas-1.13.0/src/errcode.h0000644000175000017500000001067114161102026012343 00000000000000/* * errcode.h: internal interface for error reporting * * Copyright (C) 2009-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #ifndef ERRCODE_H_ #define ERRCODE_H_ #include "internal.h" /* Include augeas.h for the error codes */ #include "augeas.h" /* * Error details in a separate struct that we can pass around */ struct error { aug_errcode_t code; int minor; char *details; /* Human readable explanation */ const char *minor_details; /* Human readable version of MINOR */ /* A dummy info of last resort; this can be used in places where * a struct info is needed but none available */ struct info *info; /* Bit of a kludge to get at struct augeas, but since struct error * is now available in a lot of places (through struct info), this * gives a convenient way to get at the overall state */ const struct augeas *aug; /* A preallocated exception so that we can throw something, even * under OOM conditions */ struct value *exn; }; void report_error(struct error *err, aug_errcode_t errcode, const char *format, ...) ATTRIBUTE_FORMAT(printf, 3, 4); void bug_on(struct error *err, const char *srcfile, int srclineno, const char *format, ...) ATTRIBUTE_FORMAT(printf, 4, 5); void reset_error(struct error *err); #define HAS_ERR(obj) ((obj)->error->code != AUG_NOERROR) #define ERR_BAIL(obj) if ((obj)->error->code != AUG_NOERROR) goto error; #define ERR_RET(obj) if ((obj)->error->code != AUG_NOERROR) return; #define ERR_NOMEM(cond, obj) \ if (cond) { \ report_error((obj)->error, AUG_ENOMEM, NULL); \ goto error; \ } #define ERR_REPORT(obj, code, fmt ...) \ report_error((obj)->error, code, ## fmt) #define ERR_THROW(cond, obj, code, fmt ...) \ do { \ if (cond) { \ report_error((obj)->error, code, ## fmt); \ goto error; \ } \ } while(0) #define ARG_CHECK(cond, obj, fmt ...) \ do { \ if (cond) { \ report_error((obj)->error, AUG_EBADARG, ## fmt); \ goto error; \ } \ } while(0) /* Assertions that use our error reporting infrastructure instead of * aborting */ #define ensure(cond, obj) \ if (!(cond)) { \ bug_on((obj)->error, __FILE__, __LINE__, NULL); \ goto error; \ } #define ensure0(cond, obj) \ if (!(cond)) { \ bug_on((obj)->error, __FILE__, __LINE__, NULL); \ return NULL; \ } #define BUG_ON(cond, obj, fmt ...) \ if (cond) { \ bug_on((obj)->error, __FILE__, __LINE__, ## fmt); \ goto error; \ } #endif /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/jmt.c0000644000175000017500000015627314161102026011516 00000000000000/* * jmt.c: Earley parser for lenses based on Jim/Mandelbaum transducers * * Copyright (C) 2009-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include "jmt.h" #include "internal.h" #include "memory.h" #include "errcode.h" /* This is an implementation of the Earley parser described in the paper * "Efficient Earley Parsing with Regular Right-hand Sides" by Trever Jim * and Yitzhak Mandelbaum. * * Since we only deal in lenses, they are both terminals and nonterminals: * recursive lenses (those with lens->recursive set) are nonterminals, and * non-recursive lenses are terminals, with the exception of non-recursive * lenses that match the empty word. Lenses are also the semantic actions * attached to a SCAN or COMPLETE during recosntruction of the parse * tree. Our SCAN makes sure that we only ever scan nonempty words - that * means that for nonrecursive lenses which match the empty word (e.g., del * [ \t]* "") we need to make sure we get a COMPLETE when the lens matched * the empty word. * * That is achieved by treating such a lens t as the construct (t|T) with * T := eps. */ /* * Data structures for the Jim/Mandelbaum parser */ #define IND_MAX UINT32_MAX struct array { size_t elem_size; ind_t used; ind_t size; void *data; }; #define array_elem(arr, ind, typ) \ (((typ *) (arr).data) + ind) #define array_for_each(i, arr) \ for(ind_t i = 0; i < (arr).used; i++) #define array_each_elem(elt, arr, typ) \ for(typ *elt = (typ *) (arr).data + 0; \ elt - (typ *) (arr).data < (arr).used; \ elt++) static void array_init(struct array *arr, size_t elem_size) { MEMZERO(arr, 1); arr->elem_size = elem_size; } ATTRIBUTE_RETURN_CHECK static int array_add(struct array *arr, ind_t *ind) { if (arr->used >= arr->size) { int r; ind_t expand = arr->size; if (expand < 8) expand = 8; r = mem_realloc_n(&(arr->data), arr->elem_size, arr->size + expand); if (r < 0) return -1; memset((char *) arr->data + arr->elem_size*arr->size, 0, arr->elem_size * expand); arr->size += expand; } *ind = arr->used; arr->used += 1; return 0; } /* Insert a new entry into the array at index IND. Shift all entries from * IND on back by one. */ ATTRIBUTE_RETURN_CHECK static int array_insert(struct array *arr, ind_t ind) { ind_t last; if (array_add(arr, &last) < 0) return -1; if (ind >= last) return 0; memmove((char *) arr->data + arr->elem_size*(ind+1), (char *) arr->data + arr->elem_size*ind, arr->elem_size * (arr->used - ind - 1)); memset((char *) arr->data + arr->elem_size*ind, 0, arr->elem_size); return 0; } static void array_release(struct array *arr) { if (arr != NULL) { free(arr->data); arr->used = arr->size = 0; } } static void array_remove(struct array *arr, ind_t ind) { char *data = arr->data; memmove(data + ind * arr->elem_size, data + (ind + 1) * arr->elem_size, (arr->used - ind - 1) * arr->elem_size); arr->used -= 1; } ATTRIBUTE_RETURN_CHECK static int array_join(struct array *dst, struct array *src) { int r; if (dst->elem_size != src->elem_size) return -1; r = mem_realloc_n(&(dst->data), dst->elem_size, dst->used + src->used); if (r < 0) return -1; memcpy(((char *) dst->data) + dst->used * dst->elem_size, src->data, src->used * src->elem_size); dst->used += src->used; dst->size = dst->used; return 0; } /* Special lens indices - these don't reference lenses, but * some pseudo actions. We stash them at the top of the range * of IND_T, with LENS_MAX giving us the maximal lens index for * a real lens */ enum trans_op { EPS = IND_MAX, CALL = EPS - 1, LENS_MAX = CALL - 1 }; struct trans { struct state *to; ind_t lens; }; struct state { struct state *next; /* Linked list for memory management */ struct array trans; /* Array of struct trans */ ind_t nret; /* Number of returned lenses */ ind_t *ret; /* The returned lenses */ ind_t num; /* Counter for num of new states */ unsigned int reachable : 1; unsigned int live : 1; }; /* Sets of states used in determinizing the NFA to a DFA */ struct nfa_state { struct state *state; /* The new state in the DFA */ struct array set; /* Set of (struct state *) in the NFA, sorted */ }; /* For recursive lenses (nonterminals), the mapping of nonterminal to * state. We also store nonrecursive lenses here; in that case, the state * will be NULL */ struct jmt_lens { struct lens *lens; struct state *state; }; /* A Jim/Mandelbaum transducer */ struct jmt { struct error *error; struct array lenses; /* Array of struct jmt_lens */ struct state *start; ind_t lens; /* The start symbol of the grammar */ ind_t state_count; }; enum item_reason { R_ROOT = 1, R_COMPLETE = R_ROOT << 1, R_PREDICT = R_COMPLETE << 1, R_SCAN = R_PREDICT << 1 }; /* The reason an item was added for parse reconstruction; can be R_SCAN, * R_COMPLETE, R_PREDICT or R_COMPLETE|R_PREDICT */ struct link { enum item_reason reason; ind_t lens; /* R_COMPLETE, R_SCAN */ ind_t from_set; /* R_COMPLETE, R_PREDICT, R_SCAN */ ind_t from_item; /* R_COMPLETE, R_PREDICT, R_SCAN */ ind_t to_item; /* R_COMPLETE */ ind_t caller; /* number of a state */ }; struct item { /* The 'classical' Earley item (state, parent) */ struct state *state; ind_t parent; /* Backlinks to why item was added */ ind_t nlinks; struct link *links; }; struct item_set { struct array items; }; struct jmt_parse { struct jmt *jmt; struct error *error; const char *text; ind_t nsets; struct item_set **sets; }; #define for_each_item(it, set) \ array_each_elem(it, (set)->items, struct item) #define for_each_trans(t, s) \ array_each_elem(t, (s)->trans, struct trans) #define parse_state(parse, ind) \ array_elem(parse->states, ind, struct state) static struct item *set_item(struct jmt_parse *parse, ind_t set, ind_t item) { ensure(parse->sets[set] != NULL, parse); ensure(item < parse->sets[set]->items.used, parse); return array_elem(parse->sets[set]->items, item, struct item); error: return NULL; } static struct state *item_state(struct jmt_parse *parse, ind_t set, ind_t item) { return set_item(parse, set, item)->state; } static struct trans *state_trans(struct state *state, ind_t t) { return array_elem(state->trans, t, struct trans); } static ind_t item_parent(struct jmt_parse *parse, ind_t set, ind_t item) { return set_item(parse, set, item)->parent; } static bool is_return(const struct state *s) { return s->nret > 0; } static struct lens *lens_of_parse(struct jmt_parse *parse, ind_t lens) { return array_elem(parse->jmt->lenses, lens, struct jmt_lens)->lens; } /* * The parser */ /* * Manipulate the Earley graph. We denote edges in the graph as * [j, (s,i)] -> [k, item_k] => [l, item_l] * to indicate that we are adding item (s,i) to E_j and record that its * child is the item with index item_k in E_k and its sibling is item * item_l in E_l. */ /* Add item (s, k) to E_j. Note that the item was caused by action reason * using lens starting at from_item in E_{from_set} * * [j, (s,k)] -> [from_set, from_item] => [j, to_item] */ static ind_t parse_add_item(struct jmt_parse *parse, ind_t j, struct state *s, ind_t k, enum item_reason reason, ind_t lens, ind_t from_set, ind_t from_item, ind_t to_item, ind_t caller) { int r; struct item_set *set = parse->sets[j]; struct item *item = NULL; ind_t result = IND_MAX; ensure(from_item == EPS || from_item < parse->sets[from_set]->items.used, parse); ensure(to_item == EPS || to_item < parse->sets[j]->items.used, parse); if (set == NULL) { r = ALLOC(parse->sets[j]); ERR_NOMEM(r < 0, parse); array_init(&parse->sets[j]->items, sizeof(struct item)); set = parse->sets[j]; } for (ind_t i=0; i < set->items.used; i++) { if (item_state(parse, j, i) == s && item_parent(parse, j, i) == k) { result = i; item = set_item(parse, j, i); break; } } if (result == IND_MAX) { r = array_add(&set->items, &result); ERR_NOMEM(r < 0, parse); item = set_item(parse, j, result); item->state = s; item->parent = k; } for (ind_t i = 0; i < item->nlinks; i++) { struct link *lnk = item->links + i; if (lnk->reason == reason && lnk->lens == lens && lnk->from_set == from_set && lnk->from_item == from_item && lnk->to_item == to_item && lnk->caller == caller) return result; } r = REALLOC_N(item->links, item->nlinks + 1); ERR_NOMEM(r < 0, parse); struct link *lnk = item->links+ item->nlinks; item->nlinks += 1; lnk->reason = reason; lnk->lens = lens; lnk->from_set = from_set; lnk->from_item = from_item; lnk->to_item = to_item; lnk->caller = caller; error: return result; } /* Add item (s, i) to set E_j and record that it was added because * a parse of nonterminal lens, starting at itemk in E_k, was completed * by item item in E_j * * [j, (s,i)] -> [j, item] => [k, itemk] */ static void parse_add_complete(struct jmt_parse *parse, ind_t j, struct state *s, ind_t i, ind_t k, ind_t itemk, ind_t lens, ind_t item) { parse_add_item(parse, j, s, i, R_COMPLETE, lens, k, itemk, item, IND_MAX); } /* Same as parse_add_complete, but mark the item also as a predict * * [j, (s,i)] -> [j, item] => [k, itemk] */ static void parse_add_predict_complete(struct jmt_parse *parse, ind_t j, struct state *s, ind_t i, ind_t k, ind_t itemk, ind_t lens, ind_t item, ind_t caller) { parse_add_item(parse, j, s, i, R_COMPLETE|R_PREDICT, lens, k, itemk, item, caller); } /* Add item (s, j) to E_j and record that it was added because of a * prediction from item from in E_j */ static ind_t parse_add_predict(struct jmt_parse *parse, ind_t j, struct state *s, ind_t from) { ensure(from < parse->sets[j]->items.used, parse); struct state *t = item_state(parse, j, from); return parse_add_item(parse, j, s, j, R_PREDICT, EPS, j, from, EPS, t->num); error: return IND_MAX; } /* Add item (s,i) to E_j and record that it was added because of scanning * with lens starting from item item in E_k. * * [j, (s,i)] -> [k, item] */ static void parse_add_scan(struct jmt_parse *parse, ind_t j, struct state *s, ind_t i, ind_t lens, ind_t k, ind_t item) { ensure(item < parse->sets[k]->items.used, parse); parse_add_item(parse, j, s, i, R_SCAN, lens, k, item, EPS, IND_MAX); error: return; } ATTRIBUTE_PURE static bool is_complete(const struct link *lnk) { return lnk->reason & R_COMPLETE; } ATTRIBUTE_PURE static bool is_predict(const struct link *lnk) { return lnk->reason & R_PREDICT; } ATTRIBUTE_PURE static bool is_scan(const struct link *lnk) { return lnk->reason & R_SCAN; } ATTRIBUTE_PURE static bool is_last_sibling(const struct link *lnk) { if (is_complete(lnk)) return false; return lnk->reason & (R_PREDICT|R_ROOT); } ATTRIBUTE_PURE static bool returns(const struct state *s, ind_t l) { for (ind_t i = 0; i < s->nret; i++) if (s->ret[i] == l) return true; return false; } static void state_add_return(struct jmt *jmt, struct state *s, ind_t l) { int r; if (s == NULL || returns(s, l)) return; r = REALLOC_N(s->ret, s->nret + 1); ERR_NOMEM(r < 0, jmt); s->ret[s->nret] = l; s->nret += 1; error: return; } static void state_merge_returns(struct jmt *jmt, struct state *dst, const struct state *src) { for (ind_t l = 0; l < src->nret; l++) state_add_return(jmt, dst, src->ret[l]); } static void nncomplete(struct jmt_parse *parse, ind_t j, struct state *t, ind_t k, ind_t item) { for (ind_t itemk = 0; itemk < parse->sets[k]->items.used; itemk++) { struct state *u = item_state(parse, k, itemk); for_each_trans(y, u) { if (returns(t, y->lens)) { ind_t parent = item_parent(parse, k, itemk); parse_add_complete(parse, j, y->to, parent, k, itemk, y->lens, item); } } } } /* NCALLER for (t, i) in E_j, which has index item in E_j, and t -> s a * call in the transducer and s a return. The item (s,j) has index pred in * E_j */ static void ncaller(struct jmt_parse *parse, ind_t j, ind_t item, struct state *t, ind_t i, struct state *s, ind_t pred) { for_each_trans(u, t) { if (returns(s, u->lens)) { /* [j, (u->to, i)] -> [j, pred] => [j, item] */ parse_add_predict_complete(parse, j, u->to, i, j, item, u->lens, pred, t->num); } } } /* NCALLEE for (t, parent) in E_j, which has index item in E_j, and t -> s * a call in the transducer and s a return. The item (s,j) has index pred * in E_j */ static void ncallee(struct jmt_parse *parse, ind_t j, ATTRIBUTE_UNUSED ind_t item, ATTRIBUTE_UNUSED struct state *t, ATTRIBUTE_UNUSED ind_t parent, struct state *s, ind_t pred) { for_each_trans(u, s) { if (returns(s, u->lens)) { /* [j, (u->to, j)] -> [j, item] => [j, item] */ parse_add_predict_complete(parse, j, u->to, j, j, pred, u->lens, pred, t->num); } } } static struct jmt_parse *parse_init(struct jmt *jmt, const char *text, size_t text_len) { int r; struct jmt_parse *parse; r = ALLOC(parse); ERR_NOMEM(r < 0, jmt); parse->jmt = jmt; parse->error = jmt->error; parse->text = text; parse->nsets = text_len + 1; r = ALLOC_N(parse->sets, parse->nsets); ERR_NOMEM(r < 0, jmt); return parse; error: if (parse != NULL) free(parse->sets); free(parse); return NULL; } void jmt_free_parse(struct jmt_parse *parse) { if (parse == NULL) return; for (int i=0; i < parse->nsets; i++) { struct item_set *set = parse->sets[i]; if (set != NULL) { array_each_elem(x, set->items, struct item) free(x->links); array_release(&set->items); free(set); } } free(parse->sets); free(parse); } static struct state *lens_state(struct jmt *jmt, ind_t l); static void flens(FILE *fp, ind_t l) { if (l == 0) fprintf(fp, "%c", 'S'); else if (l < 'S' - 'A') fprintf(fp, "%c", 'A' + l - 1); else if (l <= 'Z' - 'A') fprintf(fp, "%c", 'A' + l); else fprintf(fp, "%u", l); } static void parse_dot_link(FILE *fp, struct jmt_parse *parse, ind_t k, struct item *x, struct link *lnk) { char *lens_label = NULL; if (is_complete(lnk) || is_scan(lnk)) { struct state *sA = lens_state(parse->jmt, lnk->lens); int r; if (sA == NULL) r = xasprintf(&lens_label, "<%d>", lnk->lens); else r = xasprintf(&lens_label, "%d", lnk->lens); if (r < 0) { fprintf(fp, "// Internal error generating lens_label\n"); return; } } fprintf(fp, " n%d_%d_%d [ label = \"(%d, %d)\"];\n", k, x->state->num, x->parent, x->state->num, x->parent); if (is_complete(lnk)) { struct item *y = set_item(parse, k, lnk->to_item); const char *pred = is_predict(lnk) ? "p" : ""; fprintf(fp, " n%d_%d_%d -> n%s%d_%d_%d [ style = dashed ];\n", k, x->state->num, x->parent, pred, k, y->state->num, y->parent); if (is_predict(lnk)) { fprintf(fp, " n%s%d_%d_%d [ label = \"\" ];\n", pred, k, y->state->num, y->parent); fprintf(fp, " n%s%d_%d_%d -> n%d_%d_%d [ style = bold ];\n", pred, k, y->state->num, y->parent, k, y->state->num, y->parent); } y = set_item(parse, lnk->from_set, lnk->from_item); fprintf(fp, " n%d_%d_%d -> n%d_%d_%d [ style = dashed, label = \"", k, x->state->num, x->parent, lnk->from_set, y->state->num, y->parent); flens(fp, lnk->lens); fprintf(fp, "\" ];\n"); } else if (is_scan(lnk)) { struct item *y = set_item(parse, lnk->from_set, lnk->from_item); fprintf(fp, " n%d_%d_%d -> n%d_%d_%d [ label = \"", k, x->state->num, x->parent, lnk->from_set, y->state->num, y->parent); for (ind_t i=lnk->from_set; i < k; i++) fprintf(fp, "%c", parse->text[i]); fprintf(fp, "\" ];\n"); } else if (is_predict(lnk)) { struct item *y = set_item(parse, lnk->from_set, lnk->from_item); fprintf(fp, " n%d_%d_%d -> n%d_%d_%d [ style = bold ];\n", k, x->state->num, x->parent, lnk->from_set, y->state->num, y->parent); } free(lens_label); } static void parse_dot(struct jmt_parse *parse, const char *fname) { FILE *fp = debug_fopen("%s", fname); if (fp == NULL) return; fprintf(fp, "digraph \"jmt_parse\" {\n"); fprintf(fp, " rankdir = RL;\n"); for (int k=0; k < parse->nsets; k++) { struct item_set *set = parse->sets[k]; if (set == NULL) continue; fprintf(fp, " subgraph \"cluster_E_%d\" {\n", k); fprintf(fp, " rankdir=RL;\n rank=same;\n"); fprintf(fp, " title%d [ label=\"E%d\", shape=plaintext ]\n", k, k); for_each_item(x, set) { for (int i=0; i < x->nlinks; i++) { struct link *lnk = x->links + i; parse_dot_link(fp, parse, k, x, lnk); } } fprintf(fp, "}\n"); } fprintf(fp, "}\n"); fclose(fp); } struct jmt_parse * jmt_parse(struct jmt *jmt, const char *text, size_t text_len) { struct jmt_parse *parse = NULL; parse = parse_init(jmt, text, text_len); ERR_BAIL(jmt); /* INIT */ parse_add_item(parse, 0, jmt->start, 0, R_ROOT, EPS, EPS, EPS, EPS, jmt->lens); /* NINIT */ if (is_return(jmt->start)) { for_each_trans(x, jmt->start) { if (returns(jmt->start, x->lens)) parse_add_predict_complete(parse, 0, x->to, 0, 0, 0, x->lens, 0, 0); } } for (int j=0; j <= text_len; j++) { struct item_set *set = parse->sets[j]; if (set == NULL) continue; for (int item=0; item < set->items.used; item++) { struct state *t = item_state(parse, j, item); ind_t i = item_parent(parse, j, item); if (is_return(t) && i != j) { /* NNCOMPLETE */ nncomplete(parse, j, t, i, item); } for_each_trans(x, t) { if (x->lens == CALL) { /* PREDICT */ ind_t pred = parse_add_predict(parse, j, x->to, item); ERR_BAIL(parse); if (is_return(x->to)) { /* NCALLER */ ncaller(parse, j, item, t, i, x->to, pred); /* NCALLEE */ ncallee(parse, j, item, t, i, x->to, pred); } } else { int count; struct lens *lens = lens_of_parse(parse, x->lens); struct state *sA = lens_state(parse->jmt, x->lens); if (! lens->recursive && sA == NULL) { /* SCAN, terminal */ // FIXME: We really need to find every k so that // text[j..k] matches lens->ctype, not just one count = regexp_match(lens->ctype, text, text_len, j, NULL); if (count > 0) { parse_add_scan(parse, j+count, x->to, i, x->lens, j, item); } } } } } } if (debugging("cf.jmt.parse")) parse_dot(parse, "jmt_parse.dot"); return parse; error: jmt_free_parse(parse); return NULL; } /* * Reconstruction of the parse tree */ static void build_nullable(struct jmt_parse *parse, ind_t pos, struct jmt_visitor *visitor, struct lens *lens, int lvl) { if (! lens->recursive) { if (visitor->terminal != NULL) { (*visitor->terminal)(lens, pos, pos, visitor->data); ERR_BAIL(parse); } } else { if (visitor->enter != NULL) { (*visitor->enter)(lens, pos, pos, visitor->data); ERR_BAIL(parse); } switch(lens->tag) { case L_REC: build_nullable(parse, pos, visitor, lens->body, lvl+1); break; case L_CONCAT: for (int i=0; i < lens->nchildren; i++) build_nullable(parse, pos, visitor, lens->children[i], lvl+1); break; case L_UNION: for (int i=0; i < lens->nchildren; i++) if (lens->children[i]->ctype_nullable) build_nullable(parse, pos, visitor, lens->children[i], lvl+1); break; case L_SUBTREE: case L_SQUARE: build_nullable(parse, pos, visitor, lens->child, lvl+1); break; case L_STAR: case L_MAYBE: break; default: BUG_ON(true, parse, "Unexpected lens tag %d", lens->tag); } if (visitor->exit != NULL) { (*visitor->exit)(lens, pos, pos, visitor->data); ERR_BAIL(parse); } } error: return; } static void build_trace(const char *msg, ind_t start, ind_t end, struct item *x, int lvl) { for (int i=0; i < lvl; i++) putc(' ', stderr); if (x != NULL) { printf("%s %d..%d: (%d, %d) %d %s%s%s\n", msg, start, end, x->state->num, x->parent, x->links->lens, is_complete(x->links) ? "c" : "", is_predict(x->links) ? "p" : "", is_scan(x->links) ? "s" : ""); } else { printf("%s %d..%d\n", msg, start, end); } } static int add_sibling(struct array *siblings, ind_t lnk) { int r; ind_t ind; r = array_add(siblings, &ind); if (r < 0) return -1; *array_elem(*siblings, ind, ind_t) = lnk; return 0; } /* Return true if CALLER is a possible caller for the link LNK which starts * at item X. * * FIXME: We can get rid of the caller field on a link if we distinguish * between NCALLER and NCALLEE in the Earley graph, rather than collapse * them both into links with reason PREDICT|COMPLETE */ static bool is_caller(struct item *x, struct link *lnk, ind_t caller) { if (lnk->reason & R_ROOT) return caller == lnk->caller; if (! is_predict(lnk)) return false; if (is_complete(lnk)) { /* NCALLER: caller == t * NCALLEE: caller == s */ return caller == lnk->caller; } /* PREDICT: caller == t || caller == s */ return caller == lnk->caller || caller == x->state->num; } /* Traverse the siblings of x and check that the callee's set of callers * contains CALLER. When a path ending with a call from CALLER exists, * record the number of the corresponding link for each item in * SIBLINGS. The links are recorded in left-to-right order, i.e. the number * of the first link to follow is in the last entry in SIBLINGS. * * Returns 0 if there is a path ending in a callee of CALLER. Return -1 if * there is none. Return -2 if there are multiple such paths. Return -3 for * any other error. */ static int filter_siblings(struct jmt_visitor *visitor, struct lens *lens, ind_t k, ind_t item, ind_t caller, struct array *siblings) { struct jmt_parse *parse = visitor->parse; struct item *x = set_item(parse, k, item); ind_t nlast = 0; int r; for (ind_t lnk = 0; lnk < x->nlinks; lnk++) if (is_last_sibling(x->links + lnk)) nlast += 1; if (nlast > 0 && nlast < x->nlinks) goto ambig; if (nlast == x->nlinks) { for (ind_t lnk = 0; lnk < x->nlinks; lnk++) { if (is_caller(x, x->links + lnk, caller)) { siblings->used = 0; r = add_sibling(siblings, lnk); if (r < 0) { ERR_REPORT(parse, AUG_ENOMEM, NULL); return -3; } return 0; } } return -1; } else { /* nlast == 0 */ ind_t found = IND_MAX; for (ind_t lnk = 0; lnk < x->nlinks; lnk++) { struct link *l = x->links + lnk; r = filter_siblings(visitor, lens, l->from_set, l->from_item, caller, siblings); if (r == -1) continue; if (r == 0) { if (found != IND_MAX) goto ambig; else found = lnk; } else { return r; } } if (found == IND_MAX) { return -1; } else { r = add_sibling(siblings, found); if (r < 0) { ERR_REPORT(parse, AUG_ENOMEM, NULL); return -3; } return 0; } } ambig: (*visitor->error)(lens, visitor->data, k, "Ambiguous parse: %d links in state (%d, %d) in E_%d", x->nlinks, x->state->num, x->parent, k); return -2; } static void visit_enter(struct jmt_visitor *visitor, struct lens *lens, size_t start, size_t end, struct item *x, int lvl) { if (debugging("cf.jmt.visit")) build_trace("{", start, end, x, lvl); if (visitor->enter != NULL) (*visitor->enter)(lens, start, end, visitor->data); } static void visit_exit(struct jmt_visitor *visitor, struct lens *lens, size_t start, size_t end, struct item *x, int lvl) { if (debugging("cf.jmt.visit")) build_trace("}", start, end, x, lvl); if (visitor->exit != NULL) (*visitor->exit)(lens, start, end, visitor->data); } static int build_children(struct jmt_parse *parse, ind_t k, ind_t item, struct jmt_visitor *visitor, int lvl, ind_t caller); static int build_tree(struct jmt_parse *parse, ind_t k, ind_t item, struct lens *lens, struct jmt_visitor *visitor, int lvl) { struct item *x = set_item(parse, k, item); ind_t start = x->links->from_set; ind_t end = k; struct item *old_x = x; if (start == end) { /* This completion corresponds to a nullable nonterminal * that match epsilon. Reconstruct the full parse tree * for matching epsilon */ if (debugging("cf.jmt.visit")) build_trace("N", x->links->from_set, k, x, lvl); build_nullable(parse, start, visitor, lens, lvl); return end; } ensure(is_complete(x->links), parse); visit_enter(visitor, lens, start, end, x, lvl); ERR_BAIL(parse); /* x is a completion item. (k, x->to_item) is its first child in the * parse tree. */ if (! is_predict(x->links)) { struct link *lnk = x->links; struct item *sib = set_item(parse, lnk->from_set, lnk->from_item); ind_t caller = sib->state->num; item = lnk->to_item; x = set_item(parse, k, item); build_children(parse, k, item, visitor, lvl, caller); ERR_BAIL(parse); } visit_exit(visitor, lens, start, end, old_x, lvl); ERR_BAIL(parse); error: return end; } static int build_children(struct jmt_parse *parse, ind_t k, ind_t item, struct jmt_visitor *visitor, int lvl, ind_t caller) { struct item *x = set_item(parse, k, item); struct lens *lens = lens_of_parse(parse, x->links->lens); struct array siblings; ind_t end = k; int r; array_init(&siblings, sizeof(ind_t)); r = filter_siblings(visitor, lens, k, item, caller, &siblings); if (r < 0) goto error; /* x the first item in a list of siblings; visit items (x->from_set, * x->from_item) in order, which will visit x and its siblings in the * parse tree from right to left */ for (ind_t i = siblings.used - 1; i > 0; i--) { ind_t lnk = *array_elem(siblings, i, ind_t); struct lens *sub = lens_of_parse(parse, x->links[lnk].lens); if (sub->recursive) { build_tree(parse, k, item, sub, visitor, lvl+1); ERR_BAIL(parse); } else { if (debugging("cf.jmt.visit")) build_trace("T", x->links->from_set, k, x, lvl+1); if (visitor->terminal != NULL) { (*visitor->terminal)(sub, x->links->from_set, k, visitor->data); ERR_BAIL(parse); } } k = x->links[lnk].from_set; item = x->links[lnk].from_item; x = set_item(parse, k, item); } error: array_release(&siblings); return end; } int jmt_visit(struct jmt_visitor *visitor, size_t *len) { struct jmt_parse *parse = visitor->parse; ind_t k = parse->nsets - 1; /* Current Earley set */ ind_t item; struct item_set *set = parse->sets[k]; if (set == NULL) goto noparse; for (item = 0; item < set->items.used; item++) { struct item *x = set_item(parse, k, item); if (x->parent == 0 && returns(x->state, parse->jmt->lens)) { for (ind_t i = 0; i < x->nlinks; i++) { if (is_complete(x->links + i) || is_scan(x->links + i)) { if (debugging("cf.jmt.visit")) printf("visit: found (%d, %d) in E_%d\n", x->state->num, x->parent, k); goto found; } } } } found: if (item >= parse->sets[k]->items.used) goto noparse; struct lens *lens = lens_of_parse(parse, parse->jmt->lens); visit_enter(visitor, lens, 0, k, NULL, 0); ERR_BAIL(parse); *len = build_children(parse, k, item, visitor, 0, parse->jmt->start->num); ERR_BAIL(parse); visit_exit(visitor, lens, 0, k, NULL, 0); ERR_BAIL(parse); return 1; error: return -1; noparse: for (; k > 0; k--) if (parse->sets[k] != NULL) break; *len = k; return 0; } /* * Build the automaton */ static struct state *make_state(struct jmt *jmt) { struct state *s; int r; r = ALLOC(s); ERR_NOMEM(r < 0, jmt); s->num = jmt->state_count++; array_init(&s->trans, sizeof(struct trans)); if (jmt->start != NULL) list_cons(jmt->start->next, s); else jmt->start = s; return s; error: return NULL; } static ind_t add_lens(struct jmt *jmt, struct lens *lens) { int r; ind_t l; struct state *sA = NULL; int nullable = 0; r = array_add(&jmt->lenses, &l); ERR_NOMEM(r < 0, jmt); ERR_NOMEM(l == IND_MAX, jmt); if (! lens->recursive) nullable = regexp_matches_empty(lens->ctype); array_elem(jmt->lenses, l, struct jmt_lens)->lens = lens; /* A nonrecursive lens that matches epsilon is both a terminal * and a nonterminal */ if (lens->recursive || nullable) { sA = make_state(jmt); ERR_NOMEM(sA == NULL, jmt); array_elem(jmt->lenses, l, struct jmt_lens)->state = sA; if (! lens->recursive) { /* Add lens again, so that l refers to the nonterminal T * for the lens, and l+1 refers to the terminal t for it */ ind_t m; r = array_add(&jmt->lenses, &m); ERR_NOMEM(r < 0, jmt); ERR_NOMEM(m == IND_MAX, jmt); array_elem(jmt->lenses, m, struct jmt_lens)->lens = lens; } } if (debugging("cf.jmt")) { if (sA == NULL) { char *s = format_lens(lens); printf("add_lens: "); print_regexp(stdout, lens->ctype); printf(" %s\n", s); free(s); } else { char *s = format_lens(lens); printf("add_lens: "); flens(stdout, l); printf(" %u %s\n", sA->num, s); if (nullable) { printf("add_lens: // %s\n", s); } free(s); } } return l; error: return IND_MAX; } static struct trans * add_new_trans(struct jmt *jmt, struct state *from, struct state *to, ind_t lens) { struct trans *t; ind_t i; int r; if (from == NULL || to == NULL) return NULL; r = array_add(&from->trans, &i); ERR_NOMEM(r < 0, jmt); t = array_elem(from->trans, i, struct trans); t->to = to; t->lens = lens; return t; error: return NULL; } static struct trans * add_eps_trans(struct jmt *jmt, struct state *from, struct state *to) { return add_new_trans(jmt, from, to, EPS); } static struct lens *lens_of_jmt(struct jmt *jmt, ind_t l) { return array_elem(jmt->lenses, l, struct jmt_lens)->lens; } static ind_t lens_index(struct jmt *jmt, struct lens *lens) { array_for_each(i, jmt->lenses) if (lens_of_jmt(jmt, i) == lens) return i; return IND_MAX; } static struct state *lens_state(struct jmt *jmt, ind_t l) { return array_elem(jmt->lenses, l, struct jmt_lens)->state; } static void print_lens_symbol(FILE *fp, struct jmt *jmt, struct lens *lens) { ind_t l = lens_index(jmt, lens); struct state *sA = lens_state(jmt, l); if (sA == NULL) print_regexp(fp, lens->ctype); else flens(fp, l); } static void print_grammar(struct jmt *jmt, struct lens *lens) { ind_t l = lens_index(jmt, lens); struct state *sA = lens_state(jmt, l); if (sA == NULL || (lens->tag == L_REC && lens->rec_internal)) return; printf(" "); print_lens_symbol(stdout, jmt, lens); printf(" := "); if (! lens->recursive) { /* Nullable regexps */ print_regexp(stdout, lens->ctype); printf("\n"); return; } switch (lens->tag) { case L_CONCAT: print_lens_symbol(stdout, jmt, lens->children[0]); for (int i=1; i < lens->nchildren; i++) { printf(" . "); print_lens_symbol(stdout, jmt, lens->children[i]); } printf("\n"); for (int i=0; i < lens->nchildren; i++) print_grammar(jmt, lens->children[i]); break; case L_UNION: print_lens_symbol(stdout, jmt, lens->children[0]); for (int i=1; i < lens->nchildren; i++) { printf(" | "); print_lens_symbol(stdout, jmt, lens->children[i]); } printf("\n"); for (int i=0; i < lens->nchildren; i++) print_grammar(jmt, lens->children[i]); break; case L_SUBTREE: print_lens_symbol(stdout, jmt, lens->child); printf("\n"); print_grammar(jmt, lens->child); break; case L_STAR: print_lens_symbol(stdout, jmt, lens->child); printf("*\n"); print_grammar(jmt, lens->child); break; case L_MAYBE: print_lens_symbol(stdout, jmt, lens->child); printf("?\n"); print_grammar(jmt, lens->child); break; case L_REC: print_lens_symbol(stdout, jmt, lens->body); printf("\n"); print_grammar(jmt, lens->body); break; case L_SQUARE: print_lens_symbol(stdout, jmt, lens->child); printf("\n"); print_grammar(jmt, lens->child); break; default: BUG_ON(true, jmt, "Unexpected lens tag %d", lens->tag); break; } error: return; } static void print_grammar_top(struct jmt *jmt, struct lens *lens) { printf("Grammar:\n"); print_grammar(jmt, lens); if (lens->tag == L_REC) { printf(" "); print_lens_symbol(stdout, jmt, lens->alias); printf(" := "); print_lens_symbol(stdout, jmt, lens->alias->body); printf("\n"); } } static void index_lenses(struct jmt *jmt, struct lens *lens) { ind_t l; l = lens_index(jmt, lens); if (l == IND_MAX) { l = add_lens(jmt, lens); ERR_BAIL(jmt); } if (! lens->recursive) return; switch (lens->tag) { case L_CONCAT: case L_UNION: for (int i=0; i < lens->nchildren; i++) index_lenses(jmt, lens->children[i]); break; case L_SUBTREE: case L_STAR: case L_MAYBE: case L_SQUARE: index_lenses(jmt, lens->child); break; case L_REC: if (! lens->rec_internal) index_lenses(jmt, lens->body); break; default: BUG_ON(true, jmt, "Unexpected lens tag %d", lens->tag); break; } error: return; } static void thompson(struct jmt *jmt, struct lens *lens, struct state **s, struct state **f) { ind_t l = lens_index(jmt, lens); struct state *sA = lens_state(jmt, l); ensure(l < jmt->lenses.used, jmt); *s = make_state(jmt); *f = make_state(jmt); ERR_BAIL(jmt); if (lens->recursive) { /* A nonterminal */ add_new_trans(jmt, *s, *f, l); add_new_trans(jmt, *s, sA, CALL); } else if (sA == NULL) { /* A terminal that never matches epsilon */ add_new_trans(jmt, *s, *f, l); } else { /* A terminal that matches epsilon */ add_new_trans(jmt, *s, *f, l); add_new_trans(jmt, *s, sA, CALL); add_new_trans(jmt, *s, *f, l+1); } error: return; } static void conv(struct jmt *jmt, struct lens *lens, struct state **s, struct state **e, struct state **f) { ind_t l = lens_index(jmt, lens); ensure(l < jmt->lenses.used, jmt); struct state *sA = lens_state(jmt, l); *s = NULL; *e = NULL; *f = NULL; if (lens->recursive) { /* A nonterminal */ *s = make_state(jmt); *f = make_state(jmt); ERR_BAIL(jmt); add_new_trans(jmt, *s, *f, l); ERR_BAIL(jmt); ensure(sA != NULL, jmt); add_new_trans(jmt, *s, sA, EPS); ERR_BAIL(jmt); } else if (sA == NULL) { /* A terminal that never matches epsilon */ *s = make_state(jmt); *f = make_state(jmt); ERR_BAIL(jmt); add_new_trans(jmt, *s, *f, l); ERR_BAIL(jmt); } else { /* A terminal that matches epsilon */ *s = make_state(jmt); *f = make_state(jmt); ERR_BAIL(jmt); add_new_trans(jmt, *s, *f, l); add_new_trans(jmt, *s, *f, l+1); add_new_trans(jmt, *s, sA, EPS); ERR_BAIL(jmt); } error: return; } static void conv_concat(struct jmt *jmt, struct lens *lens, struct state **s, struct state **e, struct state **f) { struct state *s2, *f2, *e2; conv(jmt, lens->children[0], &s2, &e2, &f2); *s = make_state(jmt); add_new_trans(jmt, *s, s2, EPS); for (int i=1; i < lens->nchildren; i++) { struct state *s3, *e3, *f3, *scall, *fcall; conv(jmt, lens->children[i], &s3, &e3, &f3); thompson(jmt, lens->children[i], &scall, &fcall); ERR_BAIL(jmt); add_eps_trans(jmt, f2, scall); add_eps_trans(jmt, e2, s3); *f = make_state(jmt); add_eps_trans(jmt, f3, *f); add_eps_trans(jmt, fcall, *f); *e = make_state(jmt); add_eps_trans(jmt, e3, *e); f2 = *f; e2 = *e; } error: return; } static void conv_union(struct jmt *jmt, struct lens *lens, struct state **s, struct state **e, struct state **f) { *s = make_state(jmt); *e = make_state(jmt); *f = make_state(jmt); ERR_BAIL(jmt); for (int i = 0; i < lens->nchildren; i++) { struct state *s2, *e2, *f2; conv(jmt, lens->children[i], &s2, &e2, &f2); ERR_BAIL(jmt); add_eps_trans(jmt, *s, s2); add_eps_trans(jmt, e2, *e); add_eps_trans(jmt, f2, *f); } error: return; } static void conv_star(struct jmt *jmt, struct lens *lens, struct state **s, struct state **e, struct state **f) { *s = make_state(jmt); *e = make_state(jmt); *f = make_state(jmt); ERR_BAIL(jmt); struct state *si, *ei, *fi, *scall, *fcall; conv(jmt, lens->child, &si, &ei, &fi); thompson(jmt, lens->child, &scall, &fcall); ERR_BAIL(jmt); add_eps_trans(jmt, *s, si); add_eps_trans(jmt, ei, si); add_eps_trans(jmt, *s, *e); add_eps_trans(jmt, ei, *e); add_eps_trans(jmt, fi, scall); add_eps_trans(jmt, fcall, scall); add_eps_trans(jmt, fi, *f); add_eps_trans(jmt, fcall, *f); ERR_BAIL(jmt); error: return; } static void conv_rhs(struct jmt *jmt, ind_t l) { struct lens *lens = lens_of_jmt(jmt, l); struct state *s = NULL, *e = NULL, *f = NULL; struct state *sA = lens_state(jmt, l); if (! lens->recursive) { /* Nothing to do for terminals that do not match epsilon */ if (sA != NULL) state_add_return(jmt, sA, l); return; } /* All other nonterminals/recursive lenses */ /* Maintain P1 */ if (lens->ctype_nullable) state_add_return(jmt, sA, l); switch (lens->tag) { case L_REC: conv(jmt, lens->body, &s, &e, &f); break; case L_CONCAT: conv_concat(jmt, lens, &s, &e, &f); break; case L_UNION: conv_union(jmt, lens, &s, &e, &f); break; case L_SUBTREE: conv(jmt, lens->child, &s, &e, &f); break; case L_STAR: conv_star(jmt, lens, &s, &e, &f); break; case L_MAYBE: conv(jmt, lens->child, &s, &e, &f); add_new_trans(jmt, s, e, EPS); break; case L_SQUARE: conv(jmt, lens->child, &s, &e, &f); break; default: BUG_ON(true, jmt, "Unexpected lens tag %d", lens->tag); } ensure(sA != NULL, jmt); add_eps_trans(jmt, sA, s); state_add_return(jmt, e, l); state_add_return(jmt, f, l); error: return; } ATTRIBUTE_RETURN_CHECK static int push_state(struct array *worklist, struct state *s) { int r; ind_t ind; r = array_add(worklist, &ind); if (r < 0) return -1; *array_elem(*worklist, ind, struct state *) = s; return 0; } static struct state *pop_state(struct array *worklist) { if (worklist->used > 0) { worklist->used -= 1; return *array_elem(*worklist, worklist->used, struct state *); } else { return NULL; } } static void free_state(struct state *s) { if (s == NULL) return; free(s->ret); array_release(&s->trans); free(s); } static void collect(struct jmt *jmt) { struct array worklist; size_t count, removed; int r; count = 0; list_for_each(s, jmt->start) { s->live = 0; s->reachable = 0; count += 1; } array_init(&worklist, sizeof(struct state *)); jmt->start->reachable = 1; for (struct state *s = jmt->start; s != NULL; s = pop_state(&worklist)) { for_each_trans(t, s) { if (! t->to->reachable) { t->to->reachable = 1; r = push_state(&worklist, t->to); ERR_NOMEM(r < 0, jmt); } } } list_for_each(s, jmt->start) if (s->reachable && is_return(s)) s->live = 1; bool changed; do { changed = false; list_for_each(s, jmt->start) { if (! s->live && s->reachable) { for_each_trans(t, s) { if (t->lens != CALL && t->to->live) { s->live = 1; changed = true; break; } } } } } while (changed); list_for_each(s, jmt->start) { if (s->live && s->reachable) { for (ind_t i = 0; i < s->trans.used; ) { struct trans *t = state_trans(s, i); if (! (t->to->live && t->to->reachable)) array_remove(&s->trans, i); else i += 1; } } } removed = 0; for (struct state *s = jmt->start; s->next != NULL; ) { struct state *p = s->next; if (p->live && p->reachable) { s = p; } else { s->next = p->next; free_state(p); removed += 1; } } error: array_release(&worklist); return; } static void dedup(struct state *s) { array_for_each(i, s->trans) { struct trans *t = state_trans(s, i); for (ind_t j = i+1; j < s->trans.used;) { struct trans *u = state_trans(s, j); if (t->to == u->to && t->lens == u->lens) array_remove(&s->trans, j); else j += 1; } } } static void unepsilon(struct jmt *jmt) { int r; if (debugging("cf.jmt.build")) jmt_dot(jmt, "jmt_10_raw.dot"); collect(jmt); /* Get rid of epsilon transitions */ bool changed; do { changed = false; list_for_each(s, jmt->start) { array_for_each(i , s->trans) { struct trans *t = state_trans(s, i); if (t->lens == EPS) { struct state *to = t->to; array_remove(&s->trans, i); r = array_join(&s->trans, &to->trans); ERR_NOMEM(r < 0, jmt); state_merge_returns(jmt, s, to); dedup(s); changed = true; } } } } while (changed); collect(jmt); if (debugging("cf.jmt.build")) jmt_dot(jmt, "jmt_20_uneps.dot"); error: return; } static bool is_deterministic(struct jmt *jmt) { list_for_each(s, jmt->start) { array_for_each(i, s->trans) { struct trans *t = state_trans(s, i); for (ind_t j = i+1; j < s->trans.used; j++) { struct trans *u = state_trans(s, j); if (t->lens == u->lens) return false; } } } return true; } static void free_nfa_state(struct nfa_state *s) { if (s == NULL) return; array_release(&s->set); free(s); } static struct nfa_state *make_nfa_state(struct jmt *jmt) { struct nfa_state *result = NULL; int r; r = ALLOC(result); ERR_NOMEM(r < 0, jmt); array_init(&result->set, sizeof(struct state *)); return result; error: FREE(result); return NULL; } static ind_t nfa_state_add(struct jmt *jmt, struct nfa_state *nfa, struct state *s) { ind_t i; int r; array_for_each(j, nfa->set) { struct state *q = *array_elem(nfa->set, j, struct state *); if (q == s) return j; } /* Keep the list of states sorted */ i = nfa->set.used; for (int j=0; j + 1 < nfa->set.used; j++) { if (s < *array_elem(nfa->set, j, struct state *)) { i = j; break; } } r = array_insert(&nfa->set, i); ERR_NOMEM(r < 0, jmt); *array_elem(nfa->set, i, struct state *) = s; return i; error: return IND_MAX; } static bool nfa_same_set(struct nfa_state *s1, struct nfa_state *s2) { if (s1->set.used != s2->set.used) return false; array_for_each(i, s1->set) { struct state *q1 = *array_elem(s1->set, i, struct state *); struct state *q2 = *array_elem(s2->set, i, struct state *); if (q1 != q2) return false; } return true; } static struct nfa_state *nfa_uniq(struct jmt *jmt, struct array *newstates, struct nfa_state *s) { ind_t ind; int r; array_each_elem(q, *newstates, struct nfa_state *) { if (nfa_same_set(s, *q)) { if (s == *q) return s; free_nfa_state(s); return *q; } } r = array_add(newstates, &ind); ERR_NOMEM(r < 0, jmt); *array_elem(*newstates, ind, struct nfa_state *) = s; if (s->state == NULL) { s->state = make_state(jmt); ERR_BAIL(jmt); } /* This makes looking at pictures easier */ if (s->set.used == 1) s->state->num = (*array_elem(s->set, 0, struct state *))->num; return s; error: return NULL; } static void det_target(struct jmt *jmt, struct array *newstates, struct nfa_state *nfas, ind_t l) { struct nfa_state *to = NULL; array_each_elem(s, nfas->set, struct state *) { for_each_trans(t, *s) { if (t->lens == l) { if (to == NULL) { to = make_nfa_state(jmt); ERR_RET(jmt); } nfa_state_add(jmt, to, t->to); ERR_RET(jmt); } } } if (to != NULL) { to = nfa_uniq(jmt, newstates, to); ERR_RET(jmt); add_new_trans(jmt, nfas->state, to->state, l); } } static void determinize(struct jmt *jmt) { struct nfa_state *ini = NULL; struct array *newstates = NULL; int r; ind_t ind, nlenses; if (is_deterministic(jmt)) return; r = ALLOC(newstates); ERR_NOMEM(r < 0, jmt); array_init(newstates, sizeof(struct nfa_state *)); nlenses = jmt->lenses.used; /* The initial state consists of just the start state */ ini = make_nfa_state(jmt); ERR_BAIL(jmt); nfa_state_add(jmt, ini, jmt->start); ERR_BAIL(jmt); /* Make a new initial state */ ini->state = make_state(jmt); ini->state->num = jmt->start->num; /* Makes lokking at pictures easier */ ERR_BAIL(jmt); jmt->start->next = ini->state->next; ini->state->next = jmt->start; jmt->start = ini->state; r = array_add(newstates, &ind); ERR_NOMEM(r < 0, jmt); *array_elem(*newstates, ind, struct nfa_state *) = ini; ini = NULL; for (ind_t i = 0; i < newstates->used; i++) { struct nfa_state *nfas = *array_elem(*newstates, i, struct nfa_state *); for (int j = 0; j < nfas->set.used; j++) { struct state *s = *array_elem(nfas->set, j, struct state *); state_merge_returns(jmt, nfas->state, s); } for (ind_t l = 0; l < nlenses; l++) { det_target(jmt, newstates, nfas, l); ERR_BAIL(jmt); } det_target(jmt, newstates, nfas, CALL); ERR_BAIL(jmt); } collect(jmt); done: if (newstates) { array_each_elem(s, *newstates, struct nfa_state *) { free_nfa_state(*s); } array_release(newstates); FREE(newstates); } free_nfa_state(ini); return; error: goto done; } struct jmt *jmt_build(struct lens *lens) { struct jmt *jmt = NULL; int r; r = ALLOC(jmt); ERR_NOMEM(r < 0, lens->info); jmt->error = lens->info->error; array_init(&jmt->lenses, sizeof(struct jmt_lens)); index_lenses(jmt, lens); if (debugging("cf.jmt")) print_grammar_top(jmt, lens); for (ind_t i=0; i < jmt->lenses.used; i++) { conv_rhs(jmt, i); ERR_BAIL(jmt); } unepsilon(jmt); ERR_BAIL(jmt); determinize(jmt); ERR_BAIL(jmt); if (debugging("cf.jmt.build")) jmt_dot(jmt, "jmt_30_dfa.dot"); return jmt; error: jmt_free(jmt); return NULL; } void jmt_free(struct jmt *jmt) { if (jmt == NULL) return; array_release(&jmt->lenses); struct state *s = jmt->start; while (s != NULL) { struct state *del = s; s = del->next; free_state(del); } free(jmt); } void jmt_dot(struct jmt *jmt, const char *fname) { FILE *fp = debug_fopen("%s", fname); if (fp == NULL) return; fprintf(fp, "digraph \"jmt\" {\n"); fprintf(fp, " rankdir = LR;\n"); list_for_each(s, jmt->start) { if (is_return(s)) { fprintf(fp, " %u [ shape = doublecircle, label = \"%u (", s->num, s->num); flens(fp, s->ret[0]); for (ind_t i = 1; i < s->nret; i++) { fprintf(fp, ", "); flens(fp, s->ret[i]); } fprintf(fp, ")\" ];\n"); } for_each_trans(t, s) { fprintf(fp, " %u -> %u ", s->num, t->to->num); if (t->lens == EPS) fprintf(fp, ";\n"); else if (t->lens == CALL) fprintf(fp, "[ label = \"call\" ];\n"); else if (lens_state(jmt, t->lens) == NULL) { struct lens *lens = lens_of_jmt(jmt, t->lens); fprintf(fp, "[ label = \""); print_regexp(fp, lens->ctype); fprintf(fp, "\" ];\n"); } else { fprintf(fp, "[ label = \""); flens(fp, t->lens); fprintf(fp, "\" ];\n"); } } } fprintf(fp, "}\n"); fclose(fp); } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/internal.c0000644000175000017500000003223614161102026012530 00000000000000/* * internal.c: internal data structures and helpers * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include #include #include #include #include "internal.h" #include "memory.h" #include "fa.h" #ifndef MIN # define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif /* Cap file reads somwhat arbitrarily at 32 MB */ #define MAX_READ_LEN (32*1024*1024) int pathjoin(char **path, int nseg, ...) { va_list ap; va_start(ap, nseg); for (int i=0; i < nseg; i++) { const char *seg = va_arg(ap, const char *); if (seg == NULL) seg = "()"; int len = strlen(seg) + 1; if (*path != NULL) { len += strlen(*path) + 1; if (REALLOC_N(*path, len) == -1) { FREE(*path); va_end(ap); return -1; } if (strlen(*path) == 0 || (*path)[strlen(*path)-1] != SEP) strcat(*path, "/"); if (seg[0] == SEP) seg += 1; strcat(*path, seg); } else { if ((*path = malloc(len)) == NULL) { va_end(ap); return -1; } strcpy(*path, seg); } } va_end(ap); return 0; } /* Like gnulib's fread_file, but read no more than the specified maximum number of bytes. If the length of the input is <= max_len, and upon error while reading that data, it works just like fread_file. Taken verbatim from libvirt's util.c */ static char * fread_file_lim (FILE *stream, size_t max_len, size_t *length) { char *buf = NULL; size_t alloc = 0; size_t size = 0; int save_errno; for (;;) { size_t count; size_t requested; if (size + BUFSIZ + 1 > alloc) { char *new_buf; alloc += alloc / 2; if (alloc < size + BUFSIZ + 1) alloc = size + BUFSIZ + 1; new_buf = realloc (buf, alloc); if (!new_buf) { save_errno = errno; break; } buf = new_buf; } /* Ensure that (size + requested <= max_len); */ requested = MIN (size < max_len ? max_len - size : 0, alloc - size - 1); count = fread (buf + size, 1, requested, stream); size += count; if (count != requested || requested == 0) { save_errno = errno; if (ferror (stream)) break; buf[size] = '\0'; *length = size; return buf; } } free (buf); errno = save_errno; return NULL; } char* xfread_file(FILE *fp) { char *result; size_t len; if (!fp) return NULL; result = fread_file_lim(fp, MAX_READ_LEN, &len); if (result != NULL && len <= MAX_READ_LEN && (int) len == len) return result; free(result); return NULL; } char* xread_file(const char *path) { FILE *fp; char *result; fp = fopen(path, "r"); if (!fp) return NULL; result = xfread_file(fp); fclose(fp); return result; } /* * Escape/unescape of string literals */ static const char *const escape_chars = "\a\b\t\n\v\f\r"; static const char *const escape_names = "abtnvfr"; char *unescape(const char *s, int len, const char *extra) { size_t size; const char *n; char *result, *t; int i; if (len < 0 || len > strlen(s)) len = strlen(s); size = 0; for (i=0; i < len; i++, size++) { if (s[i] == '\\' && strchr(escape_names, s[i+1])) { i += 1; } else if (s[i] == '\\' && extra && strchr(extra, s[i+1])) { i += 1; } } if (ALLOC_N(result, size + 1) < 0) return NULL; for (i = 0, t = result; i < len; i++, size++) { if (s[i] == '\\' && (n = strchr(escape_names, s[i+1])) != NULL) { *t++ = escape_chars[n - escape_names]; i += 1; } else if (s[i] == '\\' && extra && strchr(extra, s[i+1]) != NULL) { *t++ = s[i+1]; i += 1; } else { *t++ = s[i]; } } return result; } char *escape(const char *text, int cnt, const char *extra) { int len = 0; char *esc = NULL, *e; if (cnt < 0 || cnt > strlen(text)) cnt = strlen(text); for (int i=0; i < cnt; i++) { if (text[i] && (strchr(escape_chars, text[i]) != NULL)) len += 2; /* Escaped as '\x' */ else if (text[i] && extra && (strchr(extra, text[i]) != NULL)) len += 2; /* Escaped as '\x' */ else if (! isprint(text[i])) len += 4; /* Escaped as '\ooo' */ else len += 1; } if (ALLOC_N(esc, len+1) < 0) return NULL; e = esc; for (int i=0; i < cnt; i++) { char *p; if (text[i] && ((p = strchr(escape_chars, text[i])) != NULL)) { *e++ = '\\'; *e++ = escape_names[p - escape_chars]; } else if (text[i] && extra && (strchr(extra, text[i]) != NULL)) { *e++ = '\\'; *e++ = text[i]; } else if (! isprint(text[i])) { sprintf(e, "\\%03o", (unsigned char) text[i]); e += 4; } else { *e++ = text[i]; } } return esc; } int print_chars(FILE *out, const char *text, int cnt) { int total = 0; char *esc; if (text == NULL) { fprintf(out, "nil"); return 3; } if (cnt < 0) cnt = strlen(text); esc = escape(text, cnt, "\""); total = strlen(esc); if (out != NULL) fprintf(out, "%s", esc); free(esc); return total; } char *format_pos(const char *text, int pos) { static const int window = 28; char *buf = NULL, *left = NULL, *right = NULL; int before = pos; int llen, rlen; int r; if (before > window) before = window; left = escape(text + pos - before, before, NULL); if (left == NULL) goto done; right = escape(text + pos, window, NULL); if (right == NULL) goto done; llen = strlen(left); rlen = strlen(right); if (llen < window && rlen < window) { r = asprintf(&buf, "%*s%s|=|%s%-*s\n", window - llen, "<", left, right, window - rlen, ">"); } else if (strlen(left) < window) { r = asprintf(&buf, "%*s%s|=|%s>\n", window - llen, "<", left, right); } else if (strlen(right) < window) { r = asprintf(&buf, "<%s|=|%s%-*s\n", left, right, window - rlen, ">"); } else { r = asprintf(&buf, "<%s|=|%s>\n", left, right); } if (r < 0) { buf = NULL; } done: free(left); free(right); return buf; } void print_pos(FILE *out, const char *text, int pos) { char *format = format_pos(text, pos); if (format != NULL) { fputs(format, out); FREE(format); } } int __aug_init_memstream(struct memstream *ms) { MEMZERO(ms, 1); #if HAVE_OPEN_MEMSTREAM ms->stream = open_memstream(&(ms->buf), &(ms->size)); return ms->stream == NULL ? -1 : 0; #else ms->stream = tmpfile(); if (ms->stream == NULL) { return -1; } return 0; #endif } int __aug_close_memstream(struct memstream *ms) { #if !HAVE_OPEN_MEMSTREAM rewind(ms->stream); ms->buf = fread_file_lim(ms->stream, MAX_READ_LEN, &(ms->size)); #endif if (fclose(ms->stream) == EOF) { FREE(ms->buf); ms->size = 0; return -1; } return 0; } int tree_sibling_index(struct tree *tree) { struct tree *siblings = tree->parent->children; int cnt = 0, ind = 0; list_for_each(t, siblings) { if (streqv(t->label, tree->label)) { cnt += 1; if (t == tree) ind = cnt; } } if (cnt > 1) { return ind; } else { return 0; } } char *path_expand(struct tree *tree, const char *ppath) { char *path; const char *label; char *escaped = NULL; int r; int ind = tree_sibling_index(tree); if (ppath == NULL) ppath = ""; if (tree->label == NULL) label = "(none)"; else label = tree->label; r = pathx_escape_name(label, &escaped); if (r < 0) return NULL; if (escaped != NULL) label = escaped; if (ind > 0) { r = asprintf(&path, "%s/%s[%d]", ppath, label, ind); } else { r = asprintf(&path, "%s/%s", ppath, label); } free(escaped); if (r == -1) return NULL; return path; } char *path_of_tree(struct tree *tree) { int depth, i; struct tree *t, **anc; char *path = NULL; for (t = tree, depth = 1; ! ROOT_P(t); depth++, t = t->parent); if (ALLOC_N(anc, depth) < 0) return NULL; for (t = tree, i = depth - 1; i >= 0; i--, t = t->parent) anc[i] = t; for (i = 0; i < depth; i++) { char *p = path_expand(anc[i], path); free(path); path = p; } FREE(anc); return path; } /* User-facing path cleaning */ static char *cleanstr(char *path, const char sep) { if (path == NULL || strlen(path) == 0) return path; char *e = path + strlen(path) - 1; while (e >= path && (*e == sep || isspace(*e))) *e-- = '\0'; return path; } char *cleanpath(char *path) { if (path == NULL || strlen(path) == 0) return path; if (STREQ(path, "/")) return path; return cleanstr(path, SEP); } const char *xstrerror(int errnum, char *buf, size_t len) { #ifdef HAVE_STRERROR_R # ifdef __USE_GNU /* Annoying linux specific API contract */ return strerror_r(errnum, buf, len); # else strerror_r(errnum, buf, len); return buf; # endif #else int n = snprintf(buf, len, "errno=%d", errnum); return (0 < n && n < len ? buf : "internal error: buffer too small in xstrerror"); #endif } int xasprintf(char **strp, const char *format, ...) { va_list args; int result; va_start (args, format); result = vasprintf (strp, format, args); va_end (args); if (result < 0) *strp = NULL; return result; } /* From libvirt's src/xen/block_stats.c */ int xstrtoint64(char const *s, int base, int64_t *result) { long long int lli; char *p; errno = 0; lli = strtoll(s, &p, base); if (errno || !(*p == 0 || *p == '\n') || p == s || (int64_t) lli != lli) return -1; *result = lli; return 0; } void calc_line_ofs(const char *text, size_t pos, size_t *line, size_t *ofs) { *line = 1; *ofs = 0; for (const char *t = text; t < text + pos; t++) { *ofs += 1; if (*t == '\n') { *ofs = 0; *line += 1; } } } #if HAVE_USELOCALE int regexp_c_locale(ATTRIBUTE_UNUSED char **u, ATTRIBUTE_UNUSED size_t *len) { /* On systems with uselocale, we are ok, since we make sure that we * switch to the "C" locale any time we enter through the public API */ return 0; } #else int regexp_c_locale(char **u, size_t *len) { /* Without uselocale, we need to expand character ranges */ int r; char *s = *u; size_t s_len, u_len; if (len == NULL) { len = &u_len; s_len = strlen(s); } else { s_len = *len; } r = fa_expand_char_ranges(s, s_len, u, len); if (r != 0) { *u = s; *len = s_len; } if (r < 0) return -1; /* Syntax errors will be caught when the result is compiled */ if (r > 0) return 0; free(s); return 1; } #endif #if ENABLE_DEBUG bool debugging(const char *category) { const char *debug = getenv("AUGEAS_DEBUG"); const char *s; if (debug == NULL) return false; for (s = debug; s != NULL; ) { if (STREQLEN(s, category, strlen(category))) return true; s = strchr(s, ':'); if (s != NULL) s+=1; } return false; } FILE *debug_fopen(const char *format, ...) { va_list ap; FILE *result = NULL; const char *dir; char *name = NULL, *path = NULL; int r; dir = getenv("AUGEAS_DEBUG_DIR"); if (dir == NULL) goto error; va_start(ap, format); r = vasprintf(&name, format, ap); va_end(ap); if (r < 0) goto error; r = xasprintf(&path, "%s/%s", dir, name); if (r < 0) goto error; result = fopen(path, "w"); error: free(name); free(path); return result; } #endif /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/fa.c0000644000175000017500000036201014161102026011276 00000000000000/* * fa.c: finite automata * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ /* * This implementation follows closely the Java dk.brics.automaton package * by Anders Moeller. The project's website is * http://www.brics.dk/automaton/. * * It is by no means a complete reimplementation of that package; only a * subset of what Automaton provides is implemented here. */ #include #include #include #include #include "internal.h" #include "memory.h" #include "ref.h" #include "hash.h" #include "fa.h" #define UCHAR_NUM (UCHAR_MAX+1) #define UCHAR_MIN 0 typedef unsigned char uchar; #define E(cond) if (cond) goto error #define F(expr) if ((expr) < 0) goto error /* Which algorithm to use in FA_MINIMIZE */ int fa_minimization_algorithm = FA_MIN_HOPCROFT; /* A finite automaton. INITIAL is both the initial state and the head of * the list of all states. Any state that is allocated for this automaton * is put on this list. Dead/unreachable states are cleared from the list * at opportune times (e.g., during minimization) It's poor man's garbage * collection * * Normally, transitions are on a character range [min..max]; in * fa_as_regexp, we store regexps on transitions in the re field of each * transition. TRANS_RE indicates that we do that, and is used by fa_dot to * produce proper graphs of an automaton transitioning on regexps. * * For case-insensitive regexps (nocase == 1), the FA never has transitions * on uppercase letters [A-Z], effectively removing these letters from the * alphabet. */ struct fa { struct state *initial; int deterministic : 1; int minimal : 1; unsigned int nocase : 1; int trans_re : 1; }; /* A state in a finite automaton. Transitions are never shared between states so that we can free the list when we need to free the state */ struct state { struct state *next; hash_val_t hash; unsigned int accept : 1; unsigned int live : 1; unsigned int reachable : 1; unsigned int visited : 1; /* Used in various places to track progress */ /* Array of transitions. The TUSED first entries are used, the array has allocated room for TSIZE */ size_t tused; size_t tsize; struct trans *trans; }; /* A transition. If the input has a character in the inclusive * range [MIN, MAX], move to TO */ struct trans { struct state *to; union { struct { uchar min; uchar max; }; struct re *re; }; }; /* * Bitsets */ #define UINT_BIT (sizeof(unsigned int) * CHAR_BIT) typedef unsigned int bitset; static bitset *bitset_init(size_t nbits) { bitset *bs; if (ALLOC_N(bs, (nbits + UINT_BIT) / UINT_BIT) == -1) return NULL; return bs; } static inline void bitset_clr(bitset *bs, unsigned int bit) { bs[bit/UINT_BIT] &= ~(1 << (bit % UINT_BIT)); } static inline void bitset_set(bitset *bs, unsigned int bit) { bs[bit/UINT_BIT] |= 1 << (bit % UINT_BIT); } ATTRIBUTE_PURE static inline int bitset_get(const bitset * const bs, unsigned int bit) { return (bs[bit/UINT_BIT] >> bit % UINT_BIT) & 1; } ATTRIBUTE_PURE static inline bool bitset_disjoint(const bitset *const bs1, const bitset *const bs2, size_t nbits) { for (int i=0; i < (nbits + UINT_BIT) / UINT_BIT; i++) { if (bs1[i] & bs2[i]) return false; } return true; } static void bitset_free(bitset *bs) { free(bs); } static void bitset_negate(bitset *bs, size_t nbits) { for (int i=0; i < (nbits + UINT_BIT) / UINT_BIT; i++) bs[i] = ~ bs[i]; } /* * Representation of a parsed regular expression. The regular expression is * parsed according to the following grammar by PARSE_REGEXP: * * regexp: concat_exp ('|' regexp)? * concat_exp: repeated_exp concat_exp? * repeated_exp: simple_exp * | simple_exp '*' * | simple_exp '+' * | simple_exp '?' * | simple_exp '{' INT (',' INT)? '}' * simple_exp: char_class * | '.' * | '(' regexp ')' * | CHAR * char_class: '[' char_exp+ ']' * | '[' '^' char_exp+ ']' * char_exp: CHAR '-' CHAR * | CHAR */ enum re_type { UNION, CONCAT, CSET, CHAR, ITER, EPSILON }; #define re_unref(r) unref(r, re) struct re { ref_t ref; enum re_type type; union { struct { /* UNION, CONCAT */ struct re *exp1; struct re *exp2; }; struct { /* CSET */ bool negate; bitset *cset; /* Whether we can use character ranges when converting back * to a string */ unsigned int no_ranges:1; }; struct { /* CHAR */ uchar c; }; struct { /* ITER */ struct re *exp; int min; int max; }; }; }; /* Used to keep state of the regex parse; RX may contain NUL's */ struct re_parse { const char *rx; /* Current position in regex */ const char *rend; /* Last char of rx+ 1 */ int error; /* error code */ /* Whether new CSET's should have the no_ranges flag set */ unsigned int no_ranges:1; }; /* String with explicit length, used when converting re to string */ struct re_str { char *rx; size_t len; }; static struct re *parse_regexp(struct re_parse *parse); /* A map from a set of states to a state. */ typedef hash_t state_set_hash; static hash_val_t ptr_hash(const void *p); static const int array_initial_size = 4; static const int array_max_expansion = 128; enum state_set_init_flags { S_NONE = 0, S_SORTED = (1 << 0), S_DATA = (1 << 1) }; struct state_set { size_t size; size_t used; unsigned int sorted : 1; unsigned int with_data : 1; struct state **states; void **data; }; struct state_set_list { struct state_set_list *next; struct state_set *set; }; /* Clean up FA by removing dead transitions and states and reducing * transitions. Unreachable states are freed. The return value is the same * as FA; returning it is merely a convenience. * * Only automata in this state should be returned to the user */ ATTRIBUTE_RETURN_CHECK static int collect(struct fa *fa); ATTRIBUTE_RETURN_CHECK static int totalize(struct fa *fa); /* Print an FA into a (fairly) fixed file if the environment variable * FA_DOT_DIR is set. This code is only used for debugging */ #define FA_DOT_DIR "FA_DOT_DIR" ATTRIBUTE_UNUSED static void fa_dot_debug(struct fa *fa, const char *tag) { const char *dot_dir; static int count = 0; int r; char *fname; FILE *fp; if ((dot_dir = getenv(FA_DOT_DIR)) == NULL) return; r = asprintf(&fname, "%s/fa_%02d_%s.dot", dot_dir, count++, tag); if (r == -1) return; fp = fopen(fname, "w"); if (fp == NULL) { free(fname); return; } fa_dot(fp, fa); fclose(fp); free(fname); } static void print_char_set(struct re *set) { int from, to; if (set->negate) printf("[^"); else printf("["); for (from = UCHAR_MIN; from <= UCHAR_MAX; from = to+1) { while (bitset_get(set->cset, from) == set->negate) from += 1; if (from > UCHAR_MAX) break; for (to = from; to < UCHAR_MAX && (bitset_get(set->cset, to+1) == !set->negate); to++); if (to == from) { printf("%c", from); } else { printf("%c-%c", from, to); } } printf("]"); } ATTRIBUTE_UNUSED static void print_re(struct re *re) { switch(re->type) { case UNION: print_re(re->exp1); printf("|"); print_re(re->exp2); break; case CONCAT: print_re(re->exp1); printf("."); print_re(re->exp2); break; case CSET: print_char_set(re); break; case CHAR: printf("%c", re->c); break; case ITER: printf("("); print_re(re->exp); printf("){%d,%d}", re->min, re->max); break; case EPSILON: printf("<>"); break; default: printf("(**)"); break; } } /* * struct re_str */ static void release_re_str(struct re_str *str) { if (str == NULL) return; FREE(str->rx); str->len = 0; } static void free_re_str(struct re_str *str) { if (str == NULL) return; release_re_str(str); FREE(str); } static struct re_str *make_re_str(const char *s) { struct re_str *str; if (ALLOC(str) < 0) return NULL; if (s != NULL) { str->rx = strdup(s); str->len = strlen(s); if (str->rx == NULL) { FREE(str); return NULL; } } return str; } static int re_str_alloc(struct re_str *str) { return ALLOC_N(str->rx, str->len + 1); } /* * Memory management */ static void free_trans(struct state *s) { free(s->trans); s->trans = NULL; s->tused = s->tsize = 0; } static void gut(struct fa *fa) { list_for_each(s, fa->initial) { free_trans(s); } list_free(fa->initial); fa->initial = NULL; } void fa_free(struct fa *fa) { if (fa == NULL) return; gut(fa); free(fa); } static struct state *make_state(void) { struct state *s; if (ALLOC(s) == -1) return NULL; s->hash = ptr_hash(s); return s; } static struct state *add_state(struct fa *fa, int accept) { struct state *s = make_state(); if (s) { s->accept = accept; if (fa->initial == NULL) { fa->initial = s; } else { list_cons(fa->initial->next, s); } } return s; } #define last_trans(s) ((s)->trans + (s)->tused - 1) #define for_each_trans(t, s) \ for (struct trans *t = (s)->trans; \ (t - (s)->trans) < (s)->tused; \ t++) ATTRIBUTE_RETURN_CHECK static int add_new_trans(struct state *from, struct state *to, uchar min, uchar max) { assert(to != NULL); if (from->tused == from->tsize) { size_t tsize = from->tsize; if (tsize == 0) tsize = array_initial_size; else if (from->tsize > array_max_expansion) tsize += array_max_expansion; else tsize *= 2; if (REALLOC_N(from->trans, tsize) == -1) return -1; from->tsize = tsize; } from->trans[from->tused].to = to; from->trans[from->tused].min = min; from->trans[from->tused].max = max; from->tused += 1; return 0; } ATTRIBUTE_RETURN_CHECK static int add_epsilon_trans(struct state *from, struct state *to) { int r; from->accept |= to->accept; for_each_trans(t, to) { r = add_new_trans(from, t->to, t->min, t->max); if (r < 0) return -1; } return 0; } static void set_initial(struct fa *fa, struct state *s) { list_remove(s, fa->initial); list_cons(fa->initial, s); } /* Merge automaton FA2 into FA1. This simply adds FA2's states to FA1 and then frees FA2. It has no influence on the language accepted by FA1 */ static void fa_merge(struct fa *fa1, struct fa **fa2) { list_append(fa1->initial, (*fa2)->initial); free(*fa2); *fa2 = NULL; } /* * Operations on STATE_SET */ static void state_set_free(struct state_set *set) { if (set == NULL) return; free(set->states); free(set->data); free(set); } static int state_set_init_data(struct state_set *set) { set->with_data = 1; if (set->data == NULL) return ALLOC_N(set->data, set->size); else return 0; } /* Create a new STATE_SET with an initial size of SIZE. If SIZE is -1, use the default size ARRAY_INITIAL_SIZE. FLAGS is a bitmask indicating some options: - S_SORTED: keep the states in the set sorted by their address, and use binary search for lookups. If it is not set, entries are kept in the order in which they are added and lookups scan linearly through the set of states. - S_DATA: allocate the DATA array in the set, and keep its size in sync with the size of the STATES array. */ static struct state_set *state_set_init(int size, int flags) { struct state_set *set = NULL; F(ALLOC(set)); set->sorted = (flags & S_SORTED) ? 1 : 0; set->with_data = (flags & S_DATA) ? 1 : 0; if (size > 0) { set->size = size; F(ALLOC_N(set->states, set->size)); if (set->with_data) F(state_set_init_data(set)); } return set; error: state_set_free(set); return NULL; } ATTRIBUTE_RETURN_CHECK static int state_set_expand(struct state_set *set) { if (set->size == 0) set->size = array_initial_size; else if (set->size > array_max_expansion) set->size += array_max_expansion; else set->size *= 2; if (REALLOC_N(set->states, set->size) < 0) goto error; if (set->with_data) if (REALLOC_N(set->data, set->size) < 0) goto error; return 0; error: /* We do this to provoke a SEGV as early as possible */ FREE(set->states); FREE(set->data); return -1; } /* Return the index where S belongs in SET->STATES to keep it sorted. S may not be in SET->STATES. The returned index is in the interval [0 .. SET->USED], with the latter indicating that S is larger than all values in SET->STATES */ static int state_set_pos(const struct state_set *set, const struct state *s) { int l = 0, h = set->used; while (l < h) { int m = (l + h)/2; if (set->states[m] > s) h = m; else if (set->states[m] < s) l = m + 1; else return m; } return l; } ATTRIBUTE_RETURN_CHECK static int state_set_push(struct state_set *set, struct state *s) { if (set->size == set->used) if (state_set_expand(set) < 0) return -1; if (set->sorted) { int p = state_set_pos(set, s); if (set->size == set->used) if (state_set_expand(set) < 0) return -1; while (p < set->used && set->states[p] <= s) p += 1; if (p < set->used) { memmove(set->states + p + 1, set->states + p, sizeof(*set->states) * (set->used - p)); if (set->data != NULL) memmove(set->data + p + 1, set->data + p, sizeof(*set->data) * (set->used - p)); } set->states[p] = s; set->used += 1; return p; } else { set->states[set->used++] = s; return set->used - 1; } } ATTRIBUTE_RETURN_CHECK static int state_set_push_data(struct state_set *set, struct state *s, void *d) { int i = state_set_push(set, s); if (i == -1) return -1; set->data[i] = d; return i; } static int state_set_index(const struct state_set *set, const struct state *s) { if (set->sorted) { int p = state_set_pos(set, s); return (p < set->used && set->states[p] == s) ? p : -1; } else { for (int i=0; i < set->used; i++) { if (set->states[i] == s) return i; } } return -1; } static void state_set_remove(struct state_set *set, const struct state *s) { if (set->sorted) { int p = state_set_index(set, s); if (p == -1) return; memmove(set->states + p, set->states + p + 1, sizeof(*set->states) * (set->used - p - 1)); if (set->data != NULL) memmove(set->data + p, set->data + p + 1, sizeof(*set->data) * (set->used - p - 1)); } else { int p = state_set_index(set, s); if (p >= 0) { set->states[p] = set->states[--set->used]; } } } /* Only add S if it's not in SET yet. Return 1 if S was added, 0 if it was already in the set and -1 on error. */ ATTRIBUTE_RETURN_CHECK static int state_set_add(struct state_set *set, struct state *s) { if (set->sorted) { int p = state_set_pos(set, s); if (p < set->used && set->states[p] == s) return 0; if (set->size == set->used) if (state_set_expand(set) < 0) return -1; while (p < set->used && set->states[p] <= s) p += 1; if (p < set->used) { memmove(set->states + p + 1, set->states + p, sizeof(*set->states) * (set->used - p)); if (set->data != NULL) memmove(set->data + p + 1, set->data + p, sizeof(*set->data) * (set->used - p)); } set->states[p] = s; set->used += 1; } else { if (state_set_index(set, s) >= 0) return 0; if (state_set_push(set, s) < 0) goto error; } return 1; error: /* We do this to provoke a SEGV as early as possible */ FREE(set->states); FREE(set->data); return -1; } static struct state *state_set_pop(struct state_set *set) { struct state *s = NULL; if (set->used > 0) s = set->states[--set->used]; return s; } static struct state *state_set_pop_data(struct state_set *set, void **d) { struct state *s = NULL; s = state_set_pop(set); *d = set->data[set->used]; return s; } static void *state_set_find_data(struct state_set *set, struct state *s) { int i = state_set_index(set, s); if (i >= 0) return set->data[i]; else return NULL; } static int state_set_equal(const struct state_set *set1, const struct state_set *set2) { if (set1->used != set2->used) return 0; if (set1->sorted && set2->sorted) { for (int i = 0; i < set1->used; i++) if (set1->states[i] != set2->states[i]) return 0; return 1; } else { for (int i=0; i < set1->used; i++) if (state_set_index(set2, set1->states[i]) == -1) return 0; return 1; } } #if 0 static void state_set_compact(struct state_set *set) { while (set->used > 0 && set->states[set->used] == NULL) set->used -= 1; for (int i=0; i < set->used; i++) { if (set->states[i] == NULL) { set->used -= 1; set->states[i] = set->states[set->used]; if (set->data) set->data[i] = set->data[set->used]; } while (set->used > 0 && set->states[set->used] == NULL) set->used -= 1; } } #endif /* Add an entry (FST, SND) to SET. FST is stored in SET->STATES, and SND is stored in SET->DATA at the same index. */ ATTRIBUTE_RETURN_CHECK static int state_pair_push(struct state_set **set, struct state *fst, struct state *snd) { if (*set == NULL) *set = state_set_init(-1, S_DATA); if (*set == NULL) return -1; int i = state_set_push(*set, fst); if (i == -1) return -1; (*set)->data[i] = snd; return 0; } /* Return the index of the pair (FST, SND) in SET, or -1 if SET contains no such pair. */ static int state_pair_find(struct state_set *set, struct state *fst, struct state *snd) { for (int i=0; i < set->used; i++) if (set->states[i] == fst && set->data[i] == snd) return i; return -1; } /* Jenkins' hash for void* */ static hash_val_t ptr_hash(const void *p) { hash_val_t hash = 0; char *c = (char *) &p; for (int i=0; i < sizeof(p); i++) { hash += c[i]; hash += (hash << 10); hash ^= (hash >> 6); } hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15); return hash; } typedef hash_t state_triple_hash; static hash_val_t pair_hash(const void *key) { register struct state *const *pair = key; return pair[0]->hash + pair[1]->hash; } static int pair_cmp(const void *key1, const void *key2) { return memcmp(key1, key2, 2*sizeof(struct state *)); } static void state_triple_node_free(hnode_t *node, ATTRIBUTE_UNUSED void *ctx) { free((void *) hnode_getkey(node)); free(node); } static state_triple_hash *state_triple_init(void) { state_triple_hash *hash; hash = hash_create(HASHCOUNT_T_MAX, pair_cmp, pair_hash); if (hash == NULL) return NULL; hash_set_allocator(hash, NULL, state_triple_node_free, NULL); return hash; } ATTRIBUTE_RETURN_CHECK static int state_triple_push(state_triple_hash *hash, struct state *s1, struct state *s2, struct state *s3) { struct state **pair; if (ALLOC_N(pair, 2) < 0) return -1; pair[0] = s1; pair[1] = s2; return hash_alloc_insert(hash, pair, s3); } static struct state * state_triple_thd(state_triple_hash *hash, struct state *s1, struct state *s2) { struct state *pair[2]; hnode_t *node; pair[0] = s1; pair[1] = s2; node = hash_lookup(hash, pair); return (node == NULL) ? NULL : (struct state *) hnode_get(node); } static void state_triple_free(state_triple_hash *hash) { if (hash != NULL) { hash_free_nodes(hash); hash_destroy(hash); } } /* * State operations */ ATTRIBUTE_RETURN_CHECK static int mark_reachable(struct fa *fa) { struct state_set *worklist = state_set_init(-1, S_NONE); int result = -1; E(worklist == NULL); list_for_each(s, fa->initial) { s->reachable = 0; } fa->initial->reachable = 1; for (struct state *s = fa->initial; s != NULL; s = state_set_pop(worklist)) { for_each_trans(t, s) { if (! t->to->reachable) { t->to->reachable = 1; F(state_set_push(worklist, t->to)); } } } result = 0; error: state_set_free(worklist); return result; } /* Return all reachable states. As a sideeffect, all states have their REACHABLE flag set appropriately. */ static struct state_set *fa_states(struct fa *fa) { struct state_set *visited = state_set_init(-1, S_NONE); int r; r = mark_reachable(fa); E(visited == NULL || r < 0); list_for_each(s, fa->initial) { if (s->reachable) F(state_set_push(visited, s)); } return visited; error: state_set_free(visited); return NULL; } /* Return all reachable accepting states. As a sideeffect, all states have their REACHABLE flag set appropriately. */ static struct state_set *fa_accept_states(struct fa *fa) { struct state_set *accept = state_set_init(-1, S_NONE); int r; E(accept == NULL); r = mark_reachable(fa); E(r < 0); list_for_each(s, fa->initial) { if (s->reachable && s->accept) F(state_set_push(accept, s)); } return accept; error: state_set_free(accept); return NULL; } /* Mark all live states, i.e. states from which an accepting state can be reached. All states have their REACHABLE and LIVE flags set appropriately. */ ATTRIBUTE_RETURN_CHECK static int mark_live(struct fa *fa) { int changed; F(mark_reachable(fa)); list_for_each(s, fa->initial) { s->live = s->reachable && s->accept; } do { changed = 0; list_for_each(s, fa->initial) { if (! s->live && s->reachable) { for_each_trans(t, s) { if (t->to->live) { s->live = 1; changed = 1; break; } } } } } while (changed); return 0; error: return -1; } /* * Reverse an automaton in place. Change FA so that it accepts the * language that is the reverse of the input automaton. * * Returns a list of the new initial states of the automaton. The list must * be freed by the caller. */ static struct state_set *fa_reverse(struct fa *fa) { struct state_set *all = NULL; struct state_set *accept = NULL; int r; all = fa_states(fa); E(all == NULL); accept = fa_accept_states(fa); E(accept == NULL); F(state_set_init_data(all)); /* Reverse all transitions */ int *tused; F(ALLOC_N(tused, all->used)); for (int i=0; i < all->used; i++) { all->data[i] = all->states[i]->trans; tused[i] = all->states[i]->tused; all->states[i]->trans = NULL; all->states[i]->tsize = 0; all->states[i]->tused = 0; } for (int i=0; i < all->used; i++) { struct state *s = all->states[i]; struct trans *t = all->data[i]; s->accept = 0; for (int j=0; j < tused[i]; j++) { r = add_new_trans(t[j].to, s, t[j].min, t[j].max); if (r < 0) goto error; } free(t); } free(tused); /* Make new initial and final states */ struct state *s = add_state(fa, 0); E(s == NULL); fa->initial->accept = 1; set_initial(fa, s); for (int i=0; i < accept->used; i++) { r = add_epsilon_trans(s, accept->states[i]); if (r < 0) goto error; } fa->deterministic = 0; fa->minimal = 0; state_set_free(all); return accept; error: state_set_free(all); state_set_free(accept); return NULL; } /* * Return a sorted array of all interval start points in FA. The returned * array is a string (null terminated) */ static uchar* start_points(struct fa *fa, int *npoints) { char pointset[UCHAR_NUM]; uchar *points = NULL; F(mark_reachable(fa)); MEMZERO(pointset, UCHAR_NUM); list_for_each(s, fa->initial) { if (! s->reachable) continue; pointset[0] = 1; for_each_trans(t, s) { pointset[t->min] = 1; if (t->max < UCHAR_MAX) pointset[t->max+1] = 1; } } *npoints = 0; for(int i=0; i < UCHAR_NUM; *npoints += pointset[i], i++); F(ALLOC_N(points, *npoints+1)); for (int i=0, n=0; i < UCHAR_NUM; i++) { if (pointset[i]) points[n++] = (uchar) i; } return points; error: free(points); return NULL; } /* * Operations on STATE_SET_HASH */ static int state_set_hash_contains(state_set_hash *smap, struct state_set *set) { return hash_lookup(smap, set) != NULL; } /* * Find the set in SMAP that has the same states as SET. If the two are * different, i.e. they point to different memory locations, free SET and * return the set found in SMAP */ static struct state_set *state_set_hash_uniq(state_set_hash *smap, struct state_set *set) { hnode_t *node = hash_lookup(smap, set); const struct state_set *orig_set = hnode_getkey(node); if (orig_set != set) { state_set_free(set); } return (struct state_set *) orig_set; } static struct state *state_set_hash_get_state(state_set_hash *smap, struct state_set *set) { hnode_t *node = hash_lookup(smap, set); return (struct state *) hnode_get(node); } static hash_val_t set_hash(const void *key) { hash_val_t hash = 0; const struct state_set *set = key; for (int i = 0; i < set->used; i++) { hash += set->states[i]->hash; } return hash; } static int set_cmp(const void *key1, const void *key2) { const struct state_set *set1 = key1; const struct state_set *set2 = key2; return state_set_equal(set1, set2) ? 0 : 1; } static void set_destroy(hnode_t *node, ATTRIBUTE_UNUSED void *ctx) { struct state_set *set = (struct state_set *) hnode_getkey(node); state_set_free(set); free(node); } ATTRIBUTE_RETURN_CHECK static int state_set_hash_add(state_set_hash **smap, struct state_set *set, struct fa *fa) { if (*smap == NULL) { *smap = hash_create(HASHCOUNT_T_MAX, set_cmp, set_hash); E(*smap == NULL); hash_set_allocator(*smap, NULL, set_destroy, NULL); } struct state *s = add_state(fa, 0); E(s == NULL); F(hash_alloc_insert(*smap, set, s)); return 0; error: return -1; } static void state_set_hash_free(state_set_hash *smap, struct state_set *protect) { if (protect != NULL) { hnode_t *node = hash_lookup(smap, protect); hash_delete(smap, node); hnode_getkey(node) = NULL; set_destroy(node, NULL); } hash_free_nodes(smap); hash_destroy(smap); } static int state_set_list_add(struct state_set_list **list, struct state_set *set) { struct state_set_list *elt; if (ALLOC(elt) < 0) return -1; elt->set = set; list_cons(*list, elt); return 0; } static struct state_set *state_set_list_pop(struct state_set_list **list) { struct state_set_list *elt = *list; struct state_set *set = elt->set; *list = elt->next; free(elt); return set; } /* Compare transitions lexicographically by (to, min, reverse max) */ static int trans_to_cmp(const void *v1, const void *v2) { const struct trans *t1 = v1; const struct trans *t2 = v2; if (t1->to != t2->to) { return (t1->to < t2->to) ? -1 : 1; } if (t1->min < t2->min) return -1; if (t1->min > t2->min) return 1; if (t1->max > t2->max) return -1; return (t1->max < t2->max) ? 1 : 0; } /* Compare transitions lexicographically by (min, reverse max, to) */ static int trans_intv_cmp(const void *v1, const void *v2) { const struct trans *t1 = v1; const struct trans *t2 = v2; if (t1->min < t2->min) return -1; if (t1->min > t2->min) return 1; if (t1->max > t2->max) return -1; if (t1->max < t2->max) return 1; if (t1->to != t2->to) { return (t1->to < t2->to) ? -1 : 1; } return 0; } /* * Reduce an automaton by combining overlapping and adjacent edge intervals * with the same destination. */ static void reduce(struct fa *fa) { list_for_each(s, fa->initial) { if (s->tused == 0) continue; qsort(s->trans, s->tused, sizeof(*s->trans), trans_to_cmp); int i=0, j=1; struct trans *t = s->trans; while (j < s->tused) { if (t[i].to == t[j].to && t[j].min <= t[i].max + 1) { if (t[j].max > t[i].max) t[i].max = t[j].max; j += 1; } else { i += 1; if (i < j) memmove(s->trans + i, s->trans + j, sizeof(*s->trans) * (s->tused - j)); s->tused -= j - i; j = i + 1; } } s->tused = i+1; /* Shrink if we use less than half the allocated size */ if (s->tsize > array_initial_size && 2*s->tused < s->tsize) { int r; r = REALLOC_N(s->trans, s->tsize); if (r == 0) s->tsize = s->tused; } } } /* * Remove dead transitions from an FA; a transition is dead is it does not * lead to a live state. This also removes any states that are not * reachable any longer from FA->INITIAL. * * Returns the same FA as a convenience */ static void collect_trans(struct fa *fa) { list_for_each(s, fa->initial) { if (! s->live) { free_trans(s); } else { int i=0; while (i < s->tused) { if (! s->trans[i].to->live) { s->tused -= 1; memmove(s->trans + i, s->trans + s->tused, sizeof(*s->trans)); } else { i += 1; } } } } } static void collect_dead_states(struct fa *fa) { /* Remove all dead states and free their storage */ for (struct state *s = fa->initial; s->next != NULL; ) { if (! s->next->live) { struct state *del = s->next; s->next = del->next; free_trans(del); free(del); } else { s = s->next; } } } static int collect(struct fa *fa) { F(mark_live(fa)); if (! fa->initial->live) { /* This automaton accepts nothing, make it the canonical * epsilon automaton */ list_for_each(s, fa->initial) { free_trans(s); } list_free(fa->initial->next); fa->deterministic = 1; } else { collect_trans(fa); collect_dead_states(fa); } reduce(fa); return 0; error: return -1; } static void swap_initial(struct fa *fa) { struct state *s = fa->initial; if (s->next != NULL) { fa->initial = s->next; s->next = fa->initial->next; fa->initial->next = s; } } /* * Make a finite automaton deterministic using the given set of initial * states with the subset construction. This also eliminates dead states * and transitions and reduces and orders the transitions for each state */ static int determinize(struct fa *fa, struct state_set *ini) { int npoints; int make_ini = (ini == NULL); const uchar *points = NULL; state_set_hash *newstate = NULL; struct state_set_list *worklist = NULL; int ret = 0; if (fa->deterministic) return 0; points = start_points(fa, &npoints); E(points == NULL); if (make_ini) { ini = state_set_init(-1, S_NONE); if (ini == NULL || state_set_push(ini, fa->initial) < 0) { state_set_free(ini); goto error; } } F(state_set_list_add(&worklist, ini)); F(state_set_hash_add(&newstate, ini, fa)); // Make the new state the initial state swap_initial(fa); while (worklist != NULL) { struct state_set *sset = state_set_list_pop(&worklist); struct state *r = state_set_hash_get_state(newstate, sset); for (int q=0; q < sset->used; q++) { r->accept |= sset->states[q]->accept; } for (int n=0; n < npoints; n++) { struct state_set *pset = state_set_init(-1, S_SORTED); E(pset == NULL); for(int q=0 ; q < sset->used; q++) { for_each_trans(t, sset->states[q]) { if (t->min <= points[n] && points[n] <= t->max) { F(state_set_add(pset, t->to)); } } } if (!state_set_hash_contains(newstate, pset)) { F(state_set_list_add(&worklist, pset)); F(state_set_hash_add(&newstate, pset, fa)); } pset = state_set_hash_uniq(newstate, pset); struct state *q = state_set_hash_get_state(newstate, pset); uchar min = points[n]; uchar max = UCHAR_MAX; if (n+1 < npoints) max = points[n+1] - 1; if (add_new_trans(r, q, min, max) < 0) goto error; } } fa->deterministic = 1; done: if (newstate) state_set_hash_free(newstate, make_ini ? NULL : ini); free((void *) points); if (collect(fa) < 0) ret = -1; return ret; error: ret = -1; goto done; } /* * Minimization. As a sideeffect of minimization, the transitions are * reduced and ordered. */ static struct state *step(struct state *s, uchar c) { for_each_trans(t, s) { if (t->min <= c && c <= t->max) return t->to; } return NULL; } struct state_list { struct state_list_node *first; struct state_list_node *last; unsigned int size; }; struct state_list_node { struct state_list *sl; struct state_list_node *next; struct state_list_node *prev; struct state *state; }; static struct state_list_node *state_list_add(struct state_list *sl, struct state *s) { struct state_list_node *n; if (ALLOC(n) < 0) return NULL; n->state = s; n->sl = sl; if (sl->size++ == 0) { sl->first = n; sl->last = n; } else { sl->last->next = n; n->prev = sl->last; sl->last = n; } return n; } static void state_list_remove(struct state_list_node *n) { struct state_list *sl = n->sl; sl->size -= 1; if (sl->first == n) sl->first = n->next; else n->prev->next = n->next; if (sl->last == n) sl->last = n->prev; else n->next->prev = n->prev; free(n); } static void state_list_free(struct state_list *sl) { if (sl) list_free(sl->first); free(sl); } /* The linear index of element (q,c) in an NSTATES * NSIGMA matrix */ #define INDEX(q, c) (q * nsigma + c) static int minimize_hopcroft(struct fa *fa) { struct state_set *states = NULL; uchar *sigma = NULL; struct state_set **reverse = NULL; bitset *reverse_nonempty = NULL; struct state_set **partition = NULL; unsigned int *block = NULL; struct state_list **active = NULL; struct state_list_node **active2 = NULL; int *pending = NULL; bitset *pending2 = NULL; struct state_set *split = NULL; bitset *split2 = NULL; int *refine = NULL; bitset *refine2 = NULL; struct state_set **splitblock = NULL; struct state_set *newstates = NULL; int *nsnum = NULL; int *nsind = NULL; int result = -1; unsigned int nstates = 0; int nsigma = 0; F(determinize(fa, NULL)); /* Total automaton, nothing to do */ if (fa->initial->tused == 1 && fa->initial->trans[0].to == fa->initial && fa->initial->trans[0].min == UCHAR_MIN && fa->initial->trans[0].max == UCHAR_MAX) return 0; F(totalize(fa)); /* make arrays for numbered states and effective alphabet */ states = state_set_init(-1, S_NONE); E(states == NULL); list_for_each(s, fa->initial) { F(state_set_push(states, s)); } nstates = states->used; sigma = start_points(fa, &nsigma); E(sigma == NULL); /* initialize data structures */ /* An ss->used x nsigma matrix of lists of states */ F(ALLOC_N(reverse, nstates * nsigma)); reverse_nonempty = bitset_init(nstates * nsigma); E(reverse_nonempty == NULL); F(ALLOC_N(partition, nstates)); F(ALLOC_N(block, nstates)); F(ALLOC_N(active, nstates * nsigma)); F(ALLOC_N(active2, nstates * nsigma)); /* PENDING is an array of pairs of ints. The i'th pair is stored in * PENDING[2*i] and PENDING[2*i + 1]. There are NPENDING pairs in * PENDING at any time. SPENDING is the maximum number of pairs * allocated for PENDING. */ size_t npending = 0, spending = 0; pending2 = bitset_init(nstates * nsigma); E(pending2 == NULL); split = state_set_init(-1, S_NONE); split2 = bitset_init(nstates); E(split == NULL || split2 == NULL); F(ALLOC_N(refine, nstates)); refine2 = bitset_init(nstates); E(refine2 == NULL); F(ALLOC_N(splitblock, nstates)); for (int q = 0; q < nstates; q++) { splitblock[q] = state_set_init(-1, S_NONE); partition[q] = state_set_init(-1, S_NONE); E(splitblock[q] == NULL || partition[q] == NULL); for (int x = 0; x < nsigma; x++) { reverse[INDEX(q, x)] = state_set_init(-1, S_NONE); E(reverse[INDEX(q, x)] == NULL); F(ALLOC_N(active[INDEX(q, x)], 1)); } } /* find initial partition and reverse edges */ for (int q = 0; q < nstates; q++) { struct state *qq = states->states[q]; int j; if (qq->accept) j = 0; else j = 1; F(state_set_push(partition[j], qq)); block[q] = j; for (int x = 0; x < nsigma; x++) { uchar y = sigma[x]; struct state *p = step(qq, y); assert(p != NULL); int pn = state_set_index(states, p); assert(pn >= 0); F(state_set_push(reverse[INDEX(pn, x)], qq)); bitset_set(reverse_nonempty, INDEX(pn, x)); } } /* initialize active sets */ for (int j = 0; j <= 1; j++) for (int x = 0; x < nsigma; x++) for (int q = 0; q < partition[j]->used; q++) { struct state *qq = partition[j]->states[q]; int qn = state_set_index(states, qq); if (bitset_get(reverse_nonempty, INDEX(qn, x))) { active2[INDEX(qn, x)] = state_list_add(active[INDEX(j, x)], qq); E(active2[INDEX(qn, x)] == NULL); } } /* initialize pending */ F(ALLOC_N(pending, 2*nsigma)); npending = nsigma; spending = nsigma; for (int x = 0; x < nsigma; x++) { int a0 = active[INDEX(0,x)]->size; int a1 = active[INDEX(1,x)]->size; int j; if (a0 <= a1) j = 0; else j = 1; pending[2*x] = j; pending[2*x+1] = x; bitset_set(pending2, INDEX(j, x)); } /* process pending until fixed point */ int k = 2; while (npending-- > 0) { int p = pending[2*npending]; int x = pending[2*npending+1]; bitset_clr(pending2, INDEX(p, x)); int ref = 0; /* find states that need to be split off their blocks */ struct state_list *sh = active[INDEX(p,x)]; for (struct state_list_node *m = sh->first; m != NULL; m = m->next) { int q = state_set_index(states, m->state); struct state_set *rev = reverse[INDEX(q, x)]; for (int r =0; r < rev->used; r++) { struct state *rs = rev->states[r]; int s = state_set_index(states, rs); if (! bitset_get(split2, s)) { bitset_set(split2, s); F(state_set_push(split, rs)); int j = block[s]; F(state_set_push(splitblock[j], rs)); if (!bitset_get(refine2, j)) { bitset_set(refine2, j); refine[ref++] = j; } } } } // refine blocks for(int rr=0; rr < ref; rr++) { int j = refine[rr]; struct state_set *sp = splitblock[j]; if (sp->used < partition[j]->used) { struct state_set *b1 = partition[j]; struct state_set *b2 = partition[k]; for (int s = 0; s < sp->used; s++) { state_set_remove(b1, sp->states[s]); F(state_set_push(b2, sp->states[s])); int snum = state_set_index(states, sp->states[s]); block[snum] = k; for (int c = 0; c < nsigma; c++) { struct state_list_node *sn = active2[INDEX(snum, c)]; if (sn != NULL && sn->sl == active[INDEX(j,c)]) { state_list_remove(sn); active2[INDEX(snum, c)] = state_list_add(active[INDEX(k, c)], sp->states[s]); E(active2[INDEX(snum, c)] == NULL); } } } // update pending for (int c = 0; c < nsigma; c++) { int aj = active[INDEX(j, c)]->size; int ak = active[INDEX(k, c)]->size; if (npending + 1 > spending) { spending *= 2; F(REALLOC_N(pending, 2 * spending)); } pending[2*npending + 1] = c; if (!bitset_get(pending2, INDEX(j, c)) && 0 < aj && aj <= ak) { bitset_set(pending2, INDEX(j, c)); pending[2*npending] = j; } else { bitset_set(pending2, INDEX(k, c)); pending[2*npending] = k; } npending += 1; } k++; } for (int s = 0; s < sp->used; s++) { int snum = state_set_index(states, sp->states[s]); bitset_clr(split2, snum); } bitset_clr(refine2, j); sp->used = 0; } split->used = 0; } /* make a new state for each equivalence class, set initial state */ newstates = state_set_init(k, S_NONE); E(newstates == NULL); F(ALLOC_N(nsnum, k)); F(ALLOC_N(nsind, nstates)); for (int n = 0; n < k; n++) { struct state *s = make_state(); E(s == NULL); newstates->states[n] = s; struct state_set *partn = partition[n]; for (int q=0; q < partn->used; q++) { struct state *qs = partn->states[q]; int qnum = state_set_index(states, qs); if (qs == fa->initial) s->live = 1; /* Abuse live to flag the new initial state */ nsnum[n] = qnum; /* select representative */ nsind[qnum] = n; /* and point from partition to new state */ } } /* build transitions and set acceptance */ for (int n = 0; n < k; n++) { struct state *s = newstates->states[n]; s->accept = states->states[nsnum[n]]->accept; for_each_trans(t, states->states[nsnum[n]]) { int toind = state_set_index(states, t->to); struct state *nto = newstates->states[nsind[toind]]; F(add_new_trans(s, nto, t->min, t->max)); } } /* Get rid of old states and transitions and turn NEWTSTATES into a linked list */ gut(fa); for (int n=0; n < k; n++) if (newstates->states[n]->live) { struct state *ini = newstates->states[n]; newstates->states[n] = newstates->states[0]; newstates->states[0] = ini; } for (int n=0; n < k-1; n++) newstates->states[n]->next = newstates->states[n+1]; fa->initial = newstates->states[0]; result = 0; /* clean up */ done: free(nsind); free(nsnum); state_set_free(states); free(sigma); bitset_free(reverse_nonempty); free(block); for (int i=0; i < nstates*nsigma; i++) { if (reverse) state_set_free(reverse[i]); if (active) state_list_free(active[i]); } free(reverse); free(active); free(active2); free(pending); bitset_free(pending2); state_set_free(split); bitset_free(split2); free(refine); bitset_free(refine2); for (int q=0; q < nstates; q++) { if (splitblock) state_set_free(splitblock[q]); if (partition) state_set_free(partition[q]); } free(splitblock); free(partition); state_set_free(newstates); if (collect(fa) < 0) result = -1; return result; error: result = -1; goto done; } static int minimize_brzozowski(struct fa *fa) { struct state_set *set; /* Minimize using Brzozowski's algorithm */ set = fa_reverse(fa); E(set == NULL); F(determinize(fa, set)); state_set_free(set); set = fa_reverse(fa); E(set == NULL); F(determinize(fa, set)); state_set_free(set); return 0; error: return -1; } int fa_minimize(struct fa *fa) { int r; if (fa == NULL) return -1; if (fa->minimal) return 0; if (fa_minimization_algorithm == FA_MIN_BRZOZOWSKI) { r = minimize_brzozowski(fa); } else { r = minimize_hopcroft(fa); } if (r == 0) fa->minimal = 1; return r; } /* * Construction of fa */ static struct fa *fa_make_empty(void) { struct fa *fa; if (ALLOC(fa) < 0) return NULL; if (add_state(fa, 0) == NULL) { fa_free(fa); return NULL; } /* Even though, technically, this fa is both minimal and deterministic, * this function is also used to allocate new fa's which are then modified * further. Rather than risk erroneously marking such an fa as minimal * and deterministic, we do not do that here and take the minor hit if * that should ever need to be determined for an actual empty fa */ return fa; } static struct fa *fa_make_epsilon(void) { struct fa *fa = fa_make_empty(); if (fa) { fa->initial->accept = 1; fa->deterministic= 1; fa->minimal = 1; } return fa; } static struct fa *fa_make_char(uchar c) { struct fa *fa = fa_make_empty(); if (! fa) return NULL; struct state *s = fa->initial; struct state *t = add_state(fa, 1); int r; if (t == NULL) goto error; r = add_new_trans(s, t, c, c); if (r < 0) goto error; fa->deterministic = 1; fa->minimal = 1; return fa; error: fa_free(fa); return NULL; } struct fa *fa_make_basic(unsigned int basic) { int r; if (basic == FA_EMPTY) { return fa_make_empty(); } else if (basic == FA_EPSILON) { return fa_make_epsilon(); } else if (basic == FA_TOTAL) { struct fa *fa = fa_make_epsilon(); r = add_new_trans(fa->initial, fa->initial, UCHAR_MIN, UCHAR_MAX); if (r < 0) { fa_free(fa); fa = NULL; } return fa; } return NULL; } int fa_is_basic(struct fa *fa, unsigned int basic) { if (basic == FA_EMPTY) { return ! fa->initial->accept && fa->initial->tused == 0; } else if (basic == FA_EPSILON) { return fa->initial->accept && fa->initial->tused == 0; } else if (basic == FA_TOTAL) { if (! fa->initial->accept) return 0; if (fa->nocase) { if (fa->initial->tused != 2) return 0; struct trans *t1 = fa->initial->trans; struct trans *t2 = fa->initial->trans + 1; if (t1->to != fa->initial || t2->to != fa->initial) return 0; if (t2->max != UCHAR_MAX) { t1 = t2; t2 = fa->initial->trans; } return (t1->min == UCHAR_MIN && t1->max == 'A' - 1 && t2->min == 'Z' + 1 && t2->max == UCHAR_MAX); } else { struct trans *t = fa->initial->trans; return fa->initial->tused == 1 && t->to == fa->initial && t->min == UCHAR_MIN && t->max == UCHAR_MAX; } } return 0; } static struct fa *fa_clone(struct fa *fa) { struct fa *result = NULL; struct state_set *set = state_set_init(-1, S_DATA|S_SORTED); int r; if (fa == NULL || set == NULL || ALLOC(result) < 0) goto error; result->deterministic = fa->deterministic; result->minimal = fa->minimal; result->nocase = fa->nocase; list_for_each(s, fa->initial) { int i = state_set_push(set, s); E(i < 0); struct state *q = add_state(result, s->accept); if (q == NULL) goto error; set->data[i] = q; q->live = s->live; q->reachable = s->reachable; } for (int i=0; i < set->used; i++) { struct state *s = set->states[i]; struct state *sc = set->data[i]; for_each_trans(t, s) { int to = state_set_index(set, t->to); assert(to >= 0); struct state *toc = set->data[to]; r = add_new_trans(sc, toc, t->min, t->max); if (r < 0) goto error; } } state_set_free(set); return result; error: state_set_free(set); fa_free(result); return NULL; } static int case_expand(struct fa *fa); /* Compute FA1|FA2 and set FA1 to that automaton. FA2 is freed */ ATTRIBUTE_RETURN_CHECK static int union_in_place(struct fa *fa1, struct fa **fa2) { struct state *s; int r; if (fa1->nocase != (*fa2)->nocase) { if (case_expand(fa1) < 0) return -1; if (case_expand(*fa2) < 0) return -1; } s = add_state(fa1, 0); if (s == NULL) return -1; r = add_epsilon_trans(s, fa1->initial); if (r < 0) return -1; r = add_epsilon_trans(s, (*fa2)->initial); if (r < 0) return -1; fa1->deterministic = 0; fa1->minimal = 0; fa_merge(fa1, fa2); set_initial(fa1, s); return 0; } struct fa *fa_union(struct fa *fa1, struct fa *fa2) { fa1 = fa_clone(fa1); fa2 = fa_clone(fa2); if (fa1 == NULL || fa2 == NULL) goto error; F(union_in_place(fa1, &fa2)); return fa1; error: fa_free(fa1); fa_free(fa2); return NULL; } /* Concat FA2 onto FA1; frees FA2 and changes FA1 to FA1.FA2 */ ATTRIBUTE_RETURN_CHECK static int concat_in_place(struct fa *fa1, struct fa **fa2) { int r; if (fa1->nocase != (*fa2)->nocase) { if (case_expand(fa1) < 0) return -1; if (case_expand(*fa2) < 0) return -1; } list_for_each(s, fa1->initial) { if (s->accept) { s->accept = 0; r = add_epsilon_trans(s, (*fa2)->initial); if (r < 0) return -1; } } fa1->deterministic = 0; fa1->minimal = 0; fa_merge(fa1, fa2); return 0; } struct fa *fa_concat(struct fa *fa1, struct fa *fa2) { fa1 = fa_clone(fa1); fa2 = fa_clone(fa2); if (fa1 == NULL || fa2 == NULL) goto error; F(concat_in_place(fa1, &fa2)); F(collect(fa1)); return fa1; error: fa_free(fa1); fa_free(fa2); return NULL; } static struct fa *fa_make_char_set(bitset *cset, int negate) { struct fa *fa = fa_make_empty(); if (!fa) return NULL; struct state *s = fa->initial; struct state *t = add_state(fa, 1); int from = 0; int r; if (t == NULL) goto error; while (from <= UCHAR_MAX) { while (from <= UCHAR_MAX && bitset_get(cset, from) == negate) from += 1; if (from > UCHAR_MAX) break; int to = from; while (to < UCHAR_MAX && (bitset_get(cset, to + 1) == !negate)) to += 1; r = add_new_trans(s, t, from, to); if (r < 0) goto error; from = to + 1; } fa->deterministic = 1; fa->minimal = 1; return fa; error: fa_free(fa); return NULL; } static struct fa *fa_star(struct fa *fa) { struct state *s; int r; fa = fa_clone(fa); if (fa == NULL) return NULL; s = add_state(fa, 1); if (s == NULL) goto error; r = add_epsilon_trans(s, fa->initial); if (r < 0) goto error; set_initial(fa, s); list_for_each(p, fa->initial->next) { if (p->accept) { r = add_epsilon_trans(p, s); if (r < 0) goto error; } } fa->deterministic = 0; fa->minimal = 0; return fa; error: fa_free(fa); return NULL; } /* Form the automaton (FA){N}; FA is not modified */ static struct fa *repeat(struct fa *fa, int n) { if (n == 0) { return fa_make_epsilon(); } else if (n == 1) { return fa_clone(fa); } else { struct fa *cfa = fa_clone(fa); if (cfa == NULL) return NULL; while (n > 1) { struct fa *tfa = fa_clone(fa); if (tfa == NULL) { fa_free(cfa); return NULL; } if (concat_in_place(cfa, &tfa) < 0) { fa_free(cfa); fa_free(tfa); return NULL; } n -= 1; } return cfa; } } struct fa *fa_iter(struct fa *fa, int min, int max) { int r; if (min < 0) min = 0; if (min > max && max != -1) { return fa_make_empty(); } if (max == -1) { struct fa *sfa = fa_star(fa); if (min == 0) return sfa; if (! sfa) return NULL; struct fa *cfa = repeat(fa, min); if (! cfa) { fa_free(sfa); return NULL; } if (concat_in_place(cfa, &sfa) < 0) { fa_free(sfa); fa_free(cfa); return NULL; } return cfa; } else { struct fa *cfa = NULL; max -= min; cfa = repeat(fa, min); if (cfa == NULL) return NULL; if (max > 0) { struct fa *cfa2 = fa_clone(fa); if (cfa2 == NULL) { fa_free(cfa); return NULL; } while (max > 1) { struct fa *cfa3 = fa_clone(fa); if (cfa3 == NULL) { fa_free(cfa); fa_free(cfa2); return NULL; } list_for_each(s, cfa3->initial) { if (s->accept) { r = add_epsilon_trans(s, cfa2->initial); if (r < 0) { fa_free(cfa); fa_free(cfa2); fa_free(cfa3); return NULL; } } } fa_merge(cfa3, &cfa2); cfa2 = cfa3; max -= 1; } list_for_each(s, cfa->initial) { if (s->accept) { r = add_epsilon_trans(s, cfa2->initial); if (r < 0) { fa_free(cfa); fa_free(cfa2); return NULL; } } } fa_merge(cfa, &cfa2); cfa->deterministic = 0; cfa->minimal = 0; } if (collect(cfa) < 0) { fa_free(cfa); cfa = NULL; } return cfa; } } static void sort_transition_intervals(struct fa *fa) { list_for_each(s, fa->initial) { qsort(s->trans, s->tused, sizeof(*s->trans), trans_intv_cmp); } } struct fa *fa_intersect(struct fa *fa1, struct fa *fa2) { int ret; struct fa *fa = NULL; struct state_set *worklist = NULL; state_triple_hash *newstates = NULL; if (fa1 == fa2) return fa_clone(fa1); if (fa_is_basic(fa1, FA_EMPTY) || fa_is_basic(fa2, FA_EMPTY)) return fa_make_empty(); if (fa1->nocase != fa2->nocase) { F(case_expand(fa1)); F(case_expand(fa2)); } fa = fa_make_empty(); worklist = state_set_init(-1, S_NONE); newstates = state_triple_init(); if (fa == NULL || worklist == NULL || newstates == NULL) goto error; sort_transition_intervals(fa1); sort_transition_intervals(fa2); F(state_set_push(worklist, fa1->initial)); F(state_set_push(worklist, fa2->initial)); F(state_set_push(worklist, fa->initial)); F(state_triple_push(newstates, fa1->initial, fa2->initial, fa->initial)); while (worklist->used) { struct state *s = state_set_pop(worklist); struct state *p2 = state_set_pop(worklist); struct state *p1 = state_set_pop(worklist); s->accept = p1->accept && p2->accept; struct trans *t1 = p1->trans; struct trans *t2 = p2->trans; for (int n1 = 0, b2 = 0; n1 < p1->tused; n1++) { while (b2 < p2->tused && t2[b2].max < t1[n1].min) b2++; for (int n2 = b2; n2 < p2->tused && t1[n1].max >= t2[n2].min; n2++) { if (t2[n2].max >= t1[n1].min) { struct state *r = state_triple_thd(newstates, t1[n1].to, t2[n2].to); if (r == NULL) { r = add_state(fa, 0); E(r == NULL); F(state_set_push(worklist, t1[n1].to)); F(state_set_push(worklist, t2[n2].to)); F(state_set_push(worklist, r)); F(state_triple_push(newstates, t1[n1].to, t2[n2].to, r)); } char min = t1[n1].min > t2[n2].min ? t1[n1].min : t2[n2].min; char max = t1[n1].max < t2[n2].max ? t1[n1].max : t2[n2].max; ret = add_new_trans(s, r, min, max); if (ret < 0) goto error; } } } } fa->deterministic = fa1->deterministic && fa2->deterministic; fa->nocase = fa1->nocase && fa2->nocase; done: state_set_free(worklist); state_triple_free(newstates); if (fa != NULL) { if (collect(fa) < 0) { fa_free(fa); fa = NULL; } } return fa; error: fa_free(fa); fa = NULL; goto done; } int fa_contains(struct fa *fa1, struct fa *fa2) { int result = 0; struct state_set *worklist = NULL; /* List of pairs of states */ struct state_set *visited = NULL; /* List of pairs of states */ if (fa1 == NULL || fa2 == NULL) return -1; if (fa1 == fa2) return 1; F(determinize(fa2, NULL)); sort_transition_intervals(fa1); sort_transition_intervals(fa2); F(state_pair_push(&worklist, fa1->initial, fa2->initial)); F(state_pair_push(&visited, fa1->initial, fa2->initial)); while (worklist->used) { struct state *p1, *p2; void *v2; p1 = state_set_pop_data(worklist, &v2); p2 = v2; if (p1->accept && !p2->accept) goto done; struct trans *t1 = p1->trans; struct trans *t2 = p2->trans; for(int n1 = 0, b2 = 0; n1 < p1->tused; n1++) { while (b2 < p2->tused && t2[b2].max < t1[n1].min) b2++; int min1 = t1[n1].min, max1 = t1[n1].max; for (int n2 = b2; n2 < p2->tused && t1[n1].max >= t2[n2].min; n2++) { if (t2[n2].min > min1) goto done; if (t2[n2].max < CHAR_MAX) min1 = t2[n2].max + 1; else { min1 = UCHAR_MAX; max1 = UCHAR_MIN; } if (state_pair_find(visited, t1[n1].to, t2[n2].to) == -1) { F(state_pair_push(&worklist, t1[n1].to, t2[n2].to)); F(state_pair_push(&visited, t1[n1].to, t2[n2].to)); } } if (min1 <= max1) goto done; } } result = 1; done: state_set_free(worklist); state_set_free(visited); return result; error: result = -1; goto done; } static int add_crash_trans(struct fa *fa, struct state *s, struct state *crash, int min, int max) { int result; if (fa->nocase) { /* Never transition on anything in [A-Z] */ if (min > 'Z' || max < 'A') { result = add_new_trans(s, crash, min, max); } else if (min >= 'A' && max <= 'Z') { result = 0; } else if (max <= 'Z') { /* min < 'A' */ result = add_new_trans(s, crash, min, 'A' - 1); } else if (min >= 'A') { /* max > 'Z' */ result = add_new_trans(s, crash, 'Z' + 1, max); } else { /* min < 'A' && max > 'Z' */ result = add_new_trans(s, crash, min, 'A' - 1); if (result == 0) result = add_new_trans(s, crash, 'Z' + 1, max); } } else { result = add_new_trans(s, crash, min, max); } return result; } static int totalize(struct fa *fa) { int r; struct state *crash = add_state(fa, 0); E(crash == NULL); F(mark_reachable(fa)); sort_transition_intervals(fa); r = add_crash_trans(fa, crash, crash, UCHAR_MIN, UCHAR_MAX); if (r < 0) return -1; list_for_each(s, fa->initial) { int next = UCHAR_MIN; int tused = s->tused; for (int i=0; i < tused; i++) { uchar min = s->trans[i].min, max = s->trans[i].max; if (min > next) { r = add_crash_trans(fa, s, crash, next, min - 1); if (r < 0) return -1; } if (max + 1 > next) next = max + 1; } if (next <= UCHAR_MAX) { r = add_crash_trans(fa, s, crash, next, UCHAR_MAX); if (r < 0) return -1; } } return 0; error: return -1; } struct fa *fa_complement(struct fa *fa) { fa = fa_clone(fa); E(fa == NULL); F(determinize(fa, NULL)); F(totalize(fa)); list_for_each(s, fa->initial) s->accept = ! s->accept; F(collect(fa)); return fa; error: fa_free(fa); return NULL; } struct fa *fa_minus(struct fa *fa1, struct fa *fa2) { if (fa1 == NULL || fa2 == NULL) return NULL; if (fa_is_basic(fa1, FA_EMPTY) || fa1 == fa2) return fa_make_empty(); if (fa_is_basic(fa2, FA_EMPTY)) return fa_clone(fa1); struct fa *cfa2 = fa_complement(fa2); if (cfa2 == NULL) return NULL; struct fa *result = fa_intersect(fa1, cfa2); fa_free(cfa2); return result; } static int accept_to_accept(struct fa *fa) { int r; struct state *s = add_state(fa, 0); if (s == NULL) return -1; F(mark_reachable(fa)); list_for_each(a, fa->initial) { if (a->accept && a->reachable) { r = add_epsilon_trans(s, a); if (r < 0) return -1; } } set_initial(fa, s); fa->deterministic = fa->minimal = 0; return 0; error: return -1; } struct fa *fa_overlap(struct fa *fa1, struct fa *fa2) { struct fa *fa = NULL, *eps = NULL, *result = NULL; struct state_set *map = NULL; if (fa1 == NULL || fa2 == NULL) return NULL; fa1 = fa_clone(fa1); fa2 = fa_clone(fa2); if (fa1 == NULL || fa2 == NULL) goto error; if (determinize(fa1, NULL) < 0) goto error; if (accept_to_accept(fa1) < 0) goto error; map = fa_reverse(fa2); state_set_free(map); if (determinize(fa2, NULL) < 0) goto error; if (accept_to_accept(fa2) < 0) goto error; map = fa_reverse(fa2); state_set_free(map); if (determinize(fa2, NULL) < 0) goto error; fa = fa_intersect(fa1, fa2); if (fa == NULL) goto error; eps = fa_make_epsilon(); if (eps == NULL) goto error; result = fa_minus(fa, eps); if (result == NULL) goto error; error: fa_free(fa1); fa_free(fa2); fa_free(fa); fa_free(eps); return result; } int fa_equals(struct fa *fa1, struct fa *fa2) { if (fa1 == NULL || fa2 == NULL) return -1; /* fa_contains(fa1, fa2) && fa_contains(fa2, fa1) with error checking */ int c1 = fa_contains(fa1, fa2); if (c1 < 0) return -1; if (c1 == 0) return 0; return fa_contains(fa2, fa1); } static unsigned int chr_score(char c) { if (isalpha(c)) { return 2; } else if (isalnum(c)) { return 3; } else if (isprint(c)) { return 7; } else if (c == '\0') { return 10000; } else { return 100; } } static unsigned int str_score(const struct re_str *str) { unsigned int score = 0; for (int i=0; i < str->len; i++) { score += chr_score(str->rx[i]); } return score; } /* See if we get a better string for DST by appending C to SRC. If DST is * NULL or empty, always use SRC + C */ static struct re_str *string_extend(struct re_str *dst, const struct re_str *src, char c) { if (dst == NULL || dst->len == 0 || str_score(src) + chr_score(c) < str_score(dst)) { int slen = src->len; if (dst == NULL) dst = make_re_str(NULL); if (dst == NULL) return NULL; if (REALLOC_N(dst->rx, slen+2) < 0) { free(dst); return NULL; } memcpy(dst->rx, src->rx, slen); dst->rx[slen] = c; dst->rx[slen + 1] = '\0'; dst->len = slen + 1; } return dst; } static char pick_char(struct trans *t) { for (int c = t->min; c <= t->max; c++) if (isalpha(c)) return c; for (int c = t->min; c <= t->max; c++) if (isalnum(c)) return c; for (int c = t->min; c <= t->max; c++) if (isprint(c)) return c; return t->max; } /* Generate an example string for FA. Traverse all transitions and record * at each turn the "best" word found for that state. */ int fa_example(struct fa *fa, char **example, size_t *example_len) { struct re_str *word = NULL; struct state_set *path = NULL, *worklist = NULL; struct re_str *str = NULL; *example = NULL; *example_len = 0; /* Sort to avoid any ambiguity because of reordering of transitions */ sort_transition_intervals(fa); /* Map from state to string */ path = state_set_init(-1, S_DATA|S_SORTED); str = make_re_str(""); if (path == NULL || str == NULL) goto error; F(state_set_push_data(path, fa->initial, str)); str = NULL; /* List of states still to visit */ worklist = state_set_init(-1, S_NONE); if (worklist == NULL) goto error; F(state_set_push(worklist, fa->initial)); while (worklist->used) { struct state *s = state_set_pop(worklist); struct re_str *ps = state_set_find_data(path, s); for_each_trans(t, s) { char c = pick_char(t); int toind = state_set_index(path, t->to); if (toind == -1) { struct re_str *w = string_extend(NULL, ps, c); E(w == NULL); F(state_set_push(worklist, t->to)); F(state_set_push_data(path, t->to, w)); } else { path->data[toind] = string_extend(path->data[toind], ps, c); } } } for (int i=0; i < path->used; i++) { struct state *p = path->states[i]; struct re_str *ps = path->data[i]; if (p->accept && (word == NULL || word->len == 0 || (ps->len > 0 && str_score(word) > str_score(ps)))) { free_re_str(word); word = ps; } else { free_re_str(ps); } } state_set_free(path); state_set_free(worklist); if (word != NULL) { *example_len = word->len; *example = word->rx; free(word); } return 0; error: state_set_free(path); state_set_free(worklist); free_re_str(word); free_re_str(str); return -1; } struct enum_intl { int limit; int nwords; char **words; char *buf; size_t bsize; }; static int fa_enumerate_intl(struct state *s, struct enum_intl *ei, int pos) { int result = -1; if (ei->bsize <= pos + 1) { ei->bsize *= 2; F(REALLOC_N(ei->buf, ei->bsize)); } ei->buf[pos] = '\0'; for_each_trans(t, s) { if (t->to->visited) return -2; t->to->visited = 1; for (int i=t->min; i <= t->max; i++) { ei->buf[pos] = i; if (t->to->accept) { if (ei->nwords >= ei->limit) return -2; ei->words[ei->nwords] = strdup(ei->buf); E(ei->words[ei->nwords] == NULL); ei->nwords += 1; } result = fa_enumerate_intl(t->to, ei, pos+1); E(result < 0); } t->to->visited = 0; } ei->buf[pos] = '\0'; result = 0; error: return result; } int fa_enumerate(struct fa *fa, int limit, char ***words) { struct enum_intl ei; int result = -1; *words = NULL; MEMZERO(&ei, 1); ei.bsize = 8; /* Arbitrary initial size */ ei.limit = limit; F(ALLOC_N(ei.words, limit)); F(ALLOC_N(ei.buf, ei.bsize)); /* We use the visited bit to track which states we already visited * during the construction of a word to detect loops */ list_for_each(s, fa->initial) s->visited = 0; fa->initial->visited = 1; if (fa->initial->accept) { if (ei.nwords >= limit) return -2; ei.words[0] = strdup(""); E(ei.words[0] == NULL); ei.nwords = 1; } result = fa_enumerate_intl(fa->initial, &ei, 0); E(result < 0); result = ei.nwords; *words = ei.words; ei.words = NULL; done: free(ei.buf); return result; error: for (int i=0; i < ei.nwords; i++) free(ei.words[i]); free(ei.words); goto done; } /* Expand the automaton FA by replacing every transition s(c) -> p from * state s to p on character c by two transitions s(X) -> r, r(c) -> p via * a new state r. * If ADD_MARKER is true, also add for each original state s a new a loop * s(Y) -> q and q(X) -> s through a new state q. * * The end result is that an automaton accepting "a|ab" is turned into one * accepting "Xa|XaXb" if add_marker is false and "(YX)*Xa|(YX)*Xa(YX)*Xb" * when add_marker is true. * * The returned automaton is a copy of FA, FA is not modified. */ static struct fa *expand_alphabet(struct fa *fa, int add_marker, char X, char Y) { int ret; fa = fa_clone(fa); if (fa == NULL) return NULL; F(mark_reachable(fa)); list_for_each(p, fa->initial) { if (! p->reachable) continue; struct state *r = add_state(fa, 0); if (r == NULL) goto error; r->trans = p->trans; r->tused = p->tused; r->tsize = p->tsize; p->trans = NULL; p->tused = p->tsize = 0; ret = add_new_trans(p, r, X, X); if (ret < 0) goto error; if (add_marker) { struct state *q = add_state(fa, 0); ret = add_new_trans(p, q, Y, Y); if (ret < 0) goto error; ret = add_new_trans(q, p, X, X); if (ret < 0) goto error; } } return fa; error: fa_free(fa); return NULL; } static bitset *alphabet(struct fa *fa) { bitset *bs = bitset_init(UCHAR_NUM); if (bs == NULL) return NULL; list_for_each(s, fa->initial) { for (int i=0; i < s->tused; i++) { for (uint c = s->trans[i].min; c <= s->trans[i].max; c++) bitset_set(bs, c); } } return bs; } static bitset *last_chars(struct fa *fa) { bitset *bs = bitset_init(UCHAR_NUM); if (bs == NULL) return NULL; list_for_each(s, fa->initial) { for (int i=0; i < s->tused; i++) { if (s->trans[i].to->accept) { for (uint c = s->trans[i].min; c <= s->trans[i].max; c++) bitset_set(bs, c); } } } return bs; } static bitset *first_chars(struct fa *fa) { bitset *bs = bitset_init(UCHAR_NUM); struct state *s = fa->initial; if (bs == NULL) return NULL; for (int i=0; i < s->tused; i++) { for (uint c = s->trans[i].min; c <= s->trans[i].max; c++) bitset_set(bs, c); } return bs; } /* Return 1 if F1 and F2 are known to be unambiguously concatenable * according to simple heuristics. Return 0 if they need to be checked * further to decide ambiguity * Return -1 if an allocation fails */ static int is_splittable(struct fa *fa1, struct fa *fa2) { bitset *alpha1 = NULL; bitset *alpha2 = NULL; bitset *last1 = NULL; bitset *first2 = NULL; bool result = -1; alpha2 = alphabet(fa2); last1 = last_chars(fa1); if (alpha2 == NULL || last1 == NULL) goto done; if (bitset_disjoint(last1, alpha2, UCHAR_NUM)) { result = 1; goto done; } alpha1 = alphabet(fa1); first2 = first_chars(fa2); if (alpha1 == NULL || first2 == NULL) goto done; if (bitset_disjoint(first2, alpha1, UCHAR_NUM)) { result = 1; goto done; } result = 0; done: bitset_free(alpha1); bitset_free(alpha2); bitset_free(last1); bitset_free(first2); return result; } /* This algorithm is due to Anders Moeller, and can be found in class * AutomatonOperations in dk.brics.grammar */ int fa_ambig_example(struct fa *fa1, struct fa *fa2, char **upv, size_t *upv_len, char **pv, char **v) { static const char X = '\001'; static const char Y = '\002'; char *result = NULL, *s = NULL; size_t result_len = 0; int ret = -1, r; struct fa *mp = NULL, *ms = NULL, *sp = NULL, *ss = NULL, *amb = NULL; struct fa *a1f = NULL, *a1t = NULL, *a2f = NULL, *a2t = NULL; struct fa *b1 = NULL, *b2 = NULL; *upv = NULL; *upv_len = 0; if (pv != NULL) *pv = NULL; if (v != NULL) *v = NULL; r = is_splittable(fa1, fa2); if (r < 0) goto error; if (r == 1) return 0; #define Xs "\001" #define Ys "\002" #define MPs Ys Xs "(" Xs "(.|\n))+" #define MSs Ys Xs "(" Xs "(.|\n))*" #define SPs "(" Xs "(.|\n))+" Ys Xs #define SSs "(" Xs "(.|\n))*" Ys Xs /* These could become static constants */ r = fa_compile( MPs, strlen(MPs), &mp); if (r != REG_NOERROR) goto error; r = fa_compile( MSs, strlen(MSs), &ms); if (r != REG_NOERROR) goto error; r = fa_compile( SPs, strlen(SPs), &sp); if (r != REG_NOERROR) goto error; r = fa_compile( SSs, strlen(SSs), &ss); if (r != REG_NOERROR) goto error; #undef SSs #undef SPs #undef MSs #undef MPs #undef Xs #undef Ys a1f = expand_alphabet(fa1, 0, X, Y); a1t = expand_alphabet(fa1, 1, X, Y); a2f = expand_alphabet(fa2, 0, X, Y); a2t = expand_alphabet(fa2, 1, X, Y); if (a1f == NULL || a1t == NULL || a2f == NULL || a2t == NULL) goto error; /* Compute b1 = ((a1f . mp) & a1t) . ms */ if (concat_in_place(a1f, &mp) < 0) goto error; b1 = fa_intersect(a1f, a1t); if (b1 == NULL) goto error; if (concat_in_place(b1, &ms) < 0) goto error; if (fa_is_basic(b1, FA_EMPTY)) { /* We are done - amb which we take an example from below * will be empty, and there can therefore not be an ambiguity */ ret = 0; goto done; } /* Compute b2 = ss . ((sp . a2f) & a2t) */ if (concat_in_place(sp, &a2f) < 0) goto error; b2 = fa_intersect(sp, a2t); if (b2 == NULL) goto error; if (concat_in_place(ss, &b2) < 0) goto error; b2 = ss; ss = NULL; /* The automaton we are really interested in */ amb = fa_intersect(b1, b2); if (amb == NULL) goto error; size_t s_len = 0; r = fa_example(amb, &s, &s_len); if (r < 0) goto error; if (s != NULL) { char *t; result_len = (s_len-1)/2 - 1; F(ALLOC_N(result, result_len + 1)); t = result; int i = 0; for (i=0; s[2*i] == X; i++) { assert((t - result) < result_len); *t++ = s[2*i + 1]; } if (pv != NULL) *pv = t; i += 1; for ( ;s[2*i] == X; i++) { assert((t - result) < result_len); *t++ = s[2*i + 1]; } if (v != NULL) *v = t; i += 1; for (; 2*i+1 < s_len; i++) { assert((t - result) < result_len); *t++ = s[2*i + 1]; } } ret = 0; done: /* Clean up intermediate automata */ fa_free(mp); fa_free(ms); fa_free(ss); fa_free(sp); fa_free(a1f); fa_free(a1t); fa_free(a2f); fa_free(a2t); fa_free(b1); fa_free(b2); fa_free(amb); FREE(s); *upv = result; if (result != NULL) *upv_len = result_len; return ret; error: FREE(result); ret = -1; goto done; } /* * Construct an fa from a regular expression */ static struct fa *fa_from_re(struct re *re) { struct fa *result = NULL; switch(re->type) { case UNION: { result = fa_from_re(re->exp1); if (result == NULL) goto error; struct fa *fa2 = fa_from_re(re->exp2); if (fa2 == NULL) goto error; if (union_in_place(result, &fa2) < 0) goto error; } break; case CONCAT: { result = fa_from_re(re->exp1); if (result == NULL) goto error; struct fa *fa2 = fa_from_re(re->exp2); if (fa2 == NULL) goto error; if (concat_in_place(result, &fa2) < 0) goto error; } break; case CSET: result = fa_make_char_set(re->cset, re->negate); break; case ITER: { struct fa *fa = fa_from_re(re->exp); if (fa == NULL) goto error; result = fa_iter(fa, re->min, re->max); fa_free(fa); } break; case EPSILON: result = fa_make_epsilon(); break; case CHAR: result = fa_make_char(re->c); break; default: assert(0); break; } return result; error: fa_free(result); return NULL; } static void free_re(struct re *re) { if (re == NULL) return; assert(re->ref == 0); if (re->type == UNION || re->type == CONCAT) { re_unref(re->exp1); re_unref(re->exp2); } else if (re->type == ITER) { re_unref(re->exp); } else if (re->type == CSET) { bitset_free(re->cset); } free(re); } int fa_compile(const char *regexp, size_t size, struct fa **fa) { struct re *re = NULL; struct re_parse parse; *fa = NULL; parse.rx = regexp; parse.rend = regexp + size; parse.error = REG_NOERROR; re = parse_regexp(&parse); if (re == NULL) return parse.error; *fa = fa_from_re(re); re_unref(re); if (*fa == NULL || collect(*fa) < 0) parse.error = REG_ESPACE; return parse.error; } /* We represent a case-insensitive FA by using only transitions on * lower-case letters. */ int fa_nocase(struct fa *fa) { if (fa == NULL || fa->nocase) return 0; fa->nocase = 1; list_for_each(s, fa->initial) { int tused = s->tused; /* For every transition on characters in [A-Z] add a corresponding * transition on [a-z]; remove any portion covering [A-Z] */ for (int i=0; i < tused; i++) { struct trans *t = s->trans + i; int lc_min = t->min < 'A' ? 'a' : tolower(t->min); int lc_max = t->max > 'Z' ? 'z' : tolower(t->max); if (t->min > 'Z' || t->max < 'A') continue; if (t->min >= 'A' && t->max <= 'Z') { t->min = tolower(t->min); t->max = tolower(t->max); } else if (t->max <= 'Z') { /* t->min < 'A' */ t->max = 'A' - 1; F(add_new_trans(s, t->to, lc_min, lc_max)); } else if (t->min >= 'A') { /* t->max > 'Z' */ t->min = 'Z' + 1; F(add_new_trans(s, t->to, lc_min, lc_max)); } else { /* t->min < 'A' && t->max > 'Z' */ F(add_new_trans(s, t->to, 'Z' + 1, t->max)); s->trans[i].max = 'A' - 1; F(add_new_trans(s, s->trans[i].to, lc_min, lc_max)); } } } F(collect(fa)); return 0; error: return -1; } int fa_is_nocase(struct fa *fa) { return fa->nocase; } /* If FA is case-insensitive, turn it into a case-sensitive automaton by * adding transitions on upper-case letters for each existing transition on * lower-case letters */ static int case_expand(struct fa *fa) { if (! fa->nocase) return 0; fa->nocase = 0; list_for_each(s, fa->initial) { int tused = s->tused; /* For every transition on characters in [a-z] add a corresponding * transition on [A-Z] */ for (int i=0; i < tused; i++) { struct trans *t = s->trans + i; int lc_min = t->min < 'a' ? 'A' : toupper(t->min); int lc_max = t->max > 'z' ? 'Z' : toupper(t->max); if (t->min > 'z' || t->max < 'a') continue; F(add_new_trans(s, t->to, lc_min, lc_max)); } } F(collect(fa)); return 0; error: return -1; } /* * Regular expression parser */ static struct re *make_re(enum re_type type) { struct re *re; if (make_ref(re) == 0) re->type = type; return re; } static struct re *make_re_rep(struct re *exp, int min, int max) { struct re *re = make_re(ITER); if (re) { re->exp = exp; re->min = min; re->max = max; } else { re_unref(exp); } return re; } static struct re *make_re_binop(enum re_type type, struct re *exp1, struct re *exp2) { struct re *re = make_re(type); if (re) { re->exp1 = exp1; re->exp2 = exp2; } else { re_unref(exp1); re_unref(exp2); } return re; } static struct re *make_re_char(uchar c) { struct re *re = make_re(CHAR); if (re) re->c = c; return re; } static struct re *make_re_char_set(bool negate, bool no_ranges) { struct re *re = make_re(CSET); if (re) { re->negate = negate; re->no_ranges = no_ranges; re->cset = bitset_init(UCHAR_NUM); if (re->cset == NULL) re_unref(re); } return re; } static bool more(struct re_parse *parse) { return parse->rx < parse->rend; } static bool match(struct re_parse *parse, char m) { if (!more(parse)) return false; if (*parse->rx == m) { parse->rx += 1; return true; } return false; } static bool peek(struct re_parse *parse, const char *chars) { return *parse->rx != '\0' && strchr(chars, *parse->rx) != NULL; } static bool next(struct re_parse *parse, char *c) { if (!more(parse)) return false; *c = *parse->rx; parse->rx += 1; return true; } static bool parse_char(struct re_parse *parse, int quoted, char *c) { if (!more(parse)) return false; if (quoted && *parse->rx == '\\') { parse->rx += 1; return next(parse, c); } else { return next(parse, c); } } static void add_re_char(struct re *re, uchar from, uchar to) { assert(re->type == CSET); for (unsigned int c = from; c <= to; c++) bitset_set(re->cset, c); } static void parse_char_class(struct re_parse *parse, struct re *re) { if (! more(parse)) { parse->error = REG_EBRACK; goto error; } char from, to; parse_char(parse, 0, &from); to = from; if (match(parse, '-')) { if (! more(parse)) { parse->error = REG_EBRACK; goto error; } if (peek(parse, "]")) { if (from > to) { parse->error = REG_ERANGE; goto error; } add_re_char(re, from, to); add_re_char(re, '-', '-'); return; } else if (!parse_char(parse, 0, &to)) { parse->error = REG_ERANGE; goto error; } } if (from > to) { parse->error = REG_ERANGE; goto error; } add_re_char(re, from, to); error: return; } static struct re *parse_simple_exp(struct re_parse *parse) { struct re *re = NULL; if (match(parse, '[')) { bool negate = match(parse, '^'); re = make_re_char_set(negate, parse->no_ranges); if (re == NULL) { parse->error = REG_ESPACE; goto error; } parse_char_class(parse, re); if (parse->error != REG_NOERROR) goto error; while (more(parse) && ! peek(parse, "]")) { parse_char_class(parse, re); if (parse->error != REG_NOERROR) goto error; } if (! match(parse, ']')) { parse->error = REG_EBRACK; goto error; } } else if (match(parse, '(')) { if (match(parse, ')')) { re = make_re(EPSILON); if (re == NULL) { parse->error = REG_ESPACE; goto error; } } else { re = parse_regexp(parse); if (re == NULL) goto error; if (! match(parse, ')')) { parse->error = REG_EPAREN; goto error; } } } else if (match(parse, '.')) { re = make_re_char_set(1, parse->no_ranges); if (re == NULL) { parse->error = REG_ESPACE; goto error; } add_re_char(re, '\n', '\n'); } else if (more(parse)) { char c; if (!parse_char(parse, 1, &c)) { parse->error = REG_EESCAPE; goto error; } re = make_re_char(c); if (re == NULL) { parse->error = REG_ESPACE; goto error; } } else { re = make_re(EPSILON); if (re == NULL) { parse->error = REG_ESPACE; goto error; } } return re; error: re_unref(re); return NULL; } static int parse_int(struct re_parse *parse) { const char *lim; char *end; size_t used; long l; /* We need to be careful that strtoul will never access * memory beyond parse->rend */ for (lim = parse->rx; lim < parse->rend && *lim >= '0' && *lim <= '9'; lim++); if (lim < parse->rend) { l = strtoul(parse->rx, &end, 10); used = end - parse->rx; } else { char *s = strndup(parse->rx, parse->rend - parse->rx); if (s == NULL) { parse->error = REG_ESPACE; return -1; } l = strtoul(s, &end, 10); used = end - s; free(s); } if (used == 0) return -1; parse->rx += used; if ((l<0) || (l > INT_MAX)) { parse->error = REG_BADBR; return -1; } return (int) l; } static struct re *parse_repeated_exp(struct re_parse *parse) { struct re *re = parse_simple_exp(parse); if (re == NULL) goto error; if (match(parse, '?')) { re = make_re_rep(re, 0, 1); } else if (match(parse, '*')) { re = make_re_rep(re, 0, -1); } else if (match(parse, '+')) { re = make_re_rep(re, 1, -1); } else if (match(parse, '{')) { int min, max; min = parse_int(parse); if (min == -1) goto error; if (match(parse, ',')) { max = parse_int(parse); if (max == -1) max = -1; /* If it's not an int, it means 'unbounded' */ if (! match(parse, '}')) { parse->error = REG_EBRACE; goto error; } } else if (match(parse, '}')) { max = min; } else { parse->error = REG_EBRACE; goto error; } if (min > max && max != -1) { parse->error = REG_BADBR; goto error; } re = make_re_rep(re, min, max); } if (re == NULL) parse->error = REG_ESPACE; return re; error: re_unref(re); return NULL; } static struct re *parse_concat_exp(struct re_parse *parse) { struct re *re = parse_repeated_exp(parse); if (re == NULL) goto error; if (more(parse) && ! peek(parse, ")|")) { struct re *re2 = parse_concat_exp(parse); if (re2 == NULL) goto error; re = make_re_binop(CONCAT, re, re2); if (re == NULL) { parse->error = REG_ESPACE; goto error; } } return re; error: re_unref(re); return NULL; } static struct re *parse_regexp(struct re_parse *parse) { struct re *re = NULL; /* Something like (|r) */ if (peek(parse, "|")) re = make_re(EPSILON); else re = parse_concat_exp(parse); if (re == NULL) goto error; if (match(parse, '|')) { struct re *re2 = NULL; /* Something like (r|) */ if (peek(parse, ")")) re2 = make_re(EPSILON); else re2 = parse_regexp(parse); if (re2 == NULL) goto error; re = make_re_binop(UNION, re, re2); if (re == NULL) { parse->error = REG_ESPACE; goto error; } } return re; error: re_unref(re); return NULL; } /* * Convert a STRUCT RE to a string. Code is complicated by the fact that * we try to be clever and avoid unneeded parens and concatenation with * epsilon etc. */ static int re_as_string(const struct re *re, struct re_str *str); static int re_binop_count(enum re_type type, const struct re *re) { assert(type == CONCAT || type == UNION); if (re->type == type) { return re_binop_count(type, re->exp1) + re_binop_count(type, re->exp2); } else { return 1; } } static int re_binop_store(enum re_type type, const struct re *re, const struct re **list) { int pos = 0; if (type == re->type) { pos = re_binop_store(type, re->exp1, list); pos += re_binop_store(type, re->exp2, list + pos); } else { list[0] = re; pos = 1; } return pos; } static int re_union_as_string(const struct re *re, struct re_str *str) { assert(re->type == UNION); int result = -1; const struct re **res = NULL; struct re_str *strings = NULL; int nre = 0, r; nre = re_binop_count(re->type, re); r = ALLOC_N(res, nre); if (r < 0) goto done; re_binop_store(re->type, re, res); r = ALLOC_N(strings, nre); if (r < 0) goto error; str->len = 0; for (int i=0; i < nre; i++) { if (re_as_string(res[i], strings + i) < 0) goto error; str->len += strings[i].len; } str->len += nre-1; r = re_str_alloc(str); if (r < 0) goto error; char *p = str->rx; for (int i=0; i < nre; i++) { if (i>0) *p++ = '|'; memcpy(p, strings[i].rx, strings[i].len); p += strings[i].len; } result = 0; done: free(res); if (strings != NULL) { for (int i=0; i < nre; i++) release_re_str(strings + i); } free(strings); return result; error: release_re_str(str); result = -1; goto done; } ATTRIBUTE_PURE static int re_needs_parens_in_concat(const struct re *re) { return (re->type != CHAR && re->type != CSET && re->type != ITER); } static int re_concat_as_string(const struct re *re, struct re_str *str) { assert(re->type == CONCAT); const struct re **res = NULL; struct re_str *strings = NULL; int nre = 0, r; int result = -1; nre = re_binop_count(re->type, re); r = ALLOC_N(res, nre); if (r < 0) goto error; re_binop_store(re->type, re, res); r = ALLOC_N(strings, nre); if (r < 0) goto error; str->len = 0; for (int i=0; i < nre; i++) { if (res[i]->type == EPSILON) continue; if (re_as_string(res[i], strings + i) < 0) goto error; str->len += strings[i].len; if (re_needs_parens_in_concat(res[i])) str->len += 2; } r = re_str_alloc(str); if (r < 0) goto error; char *p = str->rx; for (int i=0; i < nre; i++) { if (res[i]->type == EPSILON) continue; if (re_needs_parens_in_concat(res[i])) *p++ = '('; p = memcpy(p, strings[i].rx, strings[i].len); p += strings[i].len; if (re_needs_parens_in_concat(res[i])) *p++ = ')'; } result = 0; done: free(res); if (strings != NULL) { for (int i=0; i < nre; i++) release_re_str(strings + i); } free(strings); return result; error: release_re_str(str); result = -1; goto done; } static bool cset_contains(const struct re *cset, int c) { return bitset_get(cset->cset, c) != cset->negate; } static int re_cset_as_string(const struct re *re, struct re_str *str) { const uchar rbrack = ']'; const uchar dash = '-'; const uchar nul = '\0'; static const char *const empty_set = "[]"; static const char *const total_set = "(.|\n)"; static const char *const not_newline = "."; char *s; int from, to, negate; size_t len; int incl_rbrack, incl_dash; int r; str->len = strlen(empty_set); /* We can not include NUL explicitly in a CSET since we use ordinary NUL delimited strings to represent them. That means that we need to use negated representation if NUL is to be included (and vice versa) */ negate = cset_contains(re, nul); if (negate) { for (from = UCHAR_MIN; from <= UCHAR_MAX && cset_contains(re, from); from += 1); if (from > UCHAR_MAX) { /* Special case: the set matches every character */ str->rx = strdup(total_set); goto done; } if (from == '\n') { for (from += 1; from <= UCHAR_MAX && cset_contains(re, from); from += 1); if (from > UCHAR_MAX) { /* Special case: the set matches everything but '\n' */ str->rx = strdup(not_newline); goto done; } } } /* See if ']' and '-' will be explicitly included in the character set (INCL_RBRACK, INCL_DASH) As we loop over the character set, we reset these flags if they are in the set, but not mentioned explicitly */ incl_rbrack = cset_contains(re, rbrack) != negate; incl_dash = cset_contains(re, dash) != negate; if (re->no_ranges) { for (from = UCHAR_MIN; from <= UCHAR_MAX; from++) if (cset_contains(re, from) != negate) str->len += 1; } else { for (from = UCHAR_MIN; from <= UCHAR_MAX; from = to+1) { while (from <= UCHAR_MAX && cset_contains(re, from) == negate) from += 1; if (from > UCHAR_MAX) break; for (to = from; to < UCHAR_MAX && (cset_contains(re, to+1) != negate); to++); if (to == from && (from == rbrack || from == dash)) continue; if (from == rbrack || from == dash) from += 1; if (to == rbrack || to == dash) to -= 1; len = (to == from) ? 1 : ((to == from + 1) ? 2 : 3); if (from < rbrack && rbrack < to) incl_rbrack = 0; if (from < dash && dash < to) incl_dash = 0; str->len += len; } str->len += incl_rbrack + incl_dash; } if (negate) str->len += 1; /* For the ^ */ r = re_str_alloc(str); if (r < 0) goto error; s = str->rx; *s++ = '['; if (negate) *s++ = '^'; if (incl_rbrack) *s++ = rbrack; if (re->no_ranges) { for (from = UCHAR_MIN; from <= UCHAR_MAX; from++) { if (from == rbrack || from == dash) continue; if (cset_contains(re, from) != negate) *s++ = from; } } else { for (from = UCHAR_MIN; from <= UCHAR_MAX; from = to+1) { while (from <= UCHAR_MAX && cset_contains(re, from) == negate) from += 1; if (from > UCHAR_MAX) break; for (to = from; to < UCHAR_MAX && (cset_contains(re, to+1) != negate); to++); if (to == from && (from == rbrack || from == dash)) continue; if (from == rbrack || from == dash) from += 1; if (to == rbrack || to == dash) to -= 1; if (to == from) { *s++ = from; } else if (to == from + 1) { *s++ = from; *s++ = to; } else { *s++ = from; *s++ = '-'; *s++ = to; } } } if (incl_dash) *s++ = dash; *s = ']'; done: if (str->rx == NULL) goto error; str->len = strlen(str->rx); return 0; error: release_re_str(str); return -1; } static int re_iter_as_string(const struct re *re, struct re_str *str) { const char *quant = NULL; char *iter = NULL; int r, result = -1; if (re_as_string(re->exp, str) < 0) return -1; if (re->min == 0 && re->max == -1) { quant = "*"; } else if (re->min == 1 && re->max == -1) { quant = "+"; } else if (re->min == 0 && re->max == 1) { quant = "?"; } else if (re->max == -1) { r = asprintf(&iter, "{%d,}", re->min); if (r < 0) return -1; quant = iter; } else { r = asprintf(&iter, "{%d,%d}", re->min, re->max); if (r < 0) return -1; quant = iter; } if (re->exp->type == CHAR || re->exp->type == CSET) { if (REALLOC_N(str->rx, str->len + strlen(quant) + 1) < 0) goto error; strcpy(str->rx + str->len, quant); str->len += strlen(quant); } else { /* Format '(' + str->rx ')' + quant */ if (REALLOC_N(str->rx, str->len + strlen(quant) + 1 + 2) < 0) goto error; memmove(str->rx + 1, str->rx, str->len); str->rx[0] = '('; str->rx[str->len + 1] = ')'; str->len += 2; strcpy(str->rx + str->len, quant); str->len += strlen(quant); } result = 0; done: FREE(iter); return result; error: release_re_str(str); goto done; } static int re_as_string(const struct re *re, struct re_str *str) { /* Characters that must be escaped */ static const char * const special_chars = ".()[]{}*|+?\\^$"; int result = 0; switch(re->type) { case UNION: result = re_union_as_string(re, str); break; case CONCAT: result = re_concat_as_string(re, str); break; case CSET: result = re_cset_as_string(re, str); break; case CHAR: if (re->c == '\0' || strchr(special_chars, re->c) == NULL) { if (ALLOC_N(str->rx, 2) < 0) goto error; str->rx[0] = re->c; str->len = 1; } else { if (ALLOC_N(str->rx, 3) < 0) goto error; str->rx[0] = '\\'; str->rx[1] = re->c; str->len = strlen(str->rx); } break; case ITER: result = re_iter_as_string(re, str); break; case EPSILON: if (ALLOC_N(str->rx, 3) < 0) goto error; strcpy(str->rx, "()"); str->len = strlen(str->rx); break; default: assert(0); abort(); break; } return result; error: release_re_str(str); return -1; } static int convert_trans_to_re(struct state *s) { struct re *re = NULL; size_t nto = 1; struct trans *trans = NULL; int r; if (s->tused == 0) return 0; qsort(s->trans, s->tused, sizeof(*s->trans), trans_to_cmp); for (int i = 0; i < s->tused - 1; i++) { if (s->trans[i].to != s->trans[i+1].to) nto += 1; } r = ALLOC_N(trans, nto); if (r < 0) goto error; struct state *to = s->trans[0].to; int tind = 0; for_each_trans(t, s) { if (t->to != to) { trans[tind].to = to; trans[tind].re = re; tind += 1; re = NULL; to = t->to; } if (re == NULL) { re = make_re_char_set(0, 0); if (re == NULL) goto error; } add_re_char(re, t->min, t->max); } assert(nto == tind + 1); trans[tind].to = to; trans[tind].re = re; /* Simplify CSETs with a single char to a CHAR */ for (int t=0; t < nto; t++) { int cnt = 0; uchar chr = UCHAR_MIN; for (int c = 0; c < UCHAR_NUM; c++) { if (bitset_get(trans[t].re->cset, c)) { cnt += 1; chr = c; } } if (cnt == 1) { re_unref(trans[t].re); trans[t].re = make_re_char(chr); if (trans[t].re == NULL) goto error; } } free_trans(s); s->trans = trans; s->tused = s->tsize = nto; return 0; error: if (trans) for (int i=0; i < nto; i++) unref(trans[i].re, re); free(trans); return -1; } ATTRIBUTE_RETURN_CHECK static int add_new_re_trans(struct state *s1, struct state *s2, struct re *re) { int r; r = add_new_trans(s1, s2, 0, 0); if (r < 0) return -1; last_trans(s1)->re = re; return 0; } /* Add the regular expression R1 . LOOP* . R2 to the transition from S1 to S2. */ static int re_collapse_trans(struct state *s1, struct state *s2, struct re *r1, struct re *loop, struct re *r2) { struct re *re = NULL; if (loop->type != EPSILON) { loop = make_re_rep(ref(loop), 0, -1); if (loop == NULL) goto error; } if (r1->type == EPSILON) { if (loop->type == EPSILON) { re = ref(r2); } else { re = make_re_binop(CONCAT, loop, ref(r2)); } } else { if (loop->type == EPSILON) { if (r2->type == EPSILON) { re = ref(r1); } else { re = make_re_binop(CONCAT, ref(r1), ref(r2)); } } else { re = make_re_binop(CONCAT, ref(r1), loop); if (re != NULL && r2->type != EPSILON) { re = make_re_binop(CONCAT, re, ref(r2)); } } } if (re == NULL) goto error; struct trans *t = NULL; for (t = s1->trans; t <= last_trans(s1) && t->to != s2; t += 1); if (t > last_trans(s1)) { if (add_new_re_trans(s1, s2, re) < 0) goto error; } else { if (t->re == NULL) { t->re = re; } else { t->re = make_re_binop(UNION, re, t->re); if (t->re == NULL) goto error; } } return 0; error: // FIXME: make sure we don't leak loop return -1; } static int convert_strings(struct fa *fa) { struct state_set *worklist = state_set_init(-1, S_NONE); int result = -1; E(worklist == NULL); /* Abuse hash to count indegree, reachable to mark visited states */ list_for_each(s, fa->initial) { s->hash = 0; s->reachable = 0; } list_for_each(s, fa->initial) { for_each_trans(t, s) { t->to->hash += 1; } } for (struct state *s = fa->initial; s != NULL; s = state_set_pop(worklist)) { for (int i=0; i < s->tused; i++) { struct trans *t = s->trans + i; struct state *to = t->to; while (to->hash == 1 && to->tused == 1 && ! to->accept) { if (t->re == NULL) { t->re = to->trans->re; to->trans->re = NULL; } else { t->re = make_re_binop(CONCAT, t->re, to->trans->re); if (t->re == NULL) goto error; } t->to = to->trans->to; to->tused = 0; to->hash -= 1; to = t->to; for (int j=0; j < s->tused; j++) { if (j != i && s->trans[j].to == to) { /* Combine transitions i and j; remove trans j */ t->re = make_re_binop(UNION, t->re, s->trans[j].re); if (t->re == NULL) goto error; memmove(s->trans + j, s->trans + j + 1, sizeof(s->trans[j]) * (s->tused - j - 1)); to->hash -= 1; s->tused -= 1; if (j < i) { i = i - 1; t = s->trans + i; } } } } if (! to->reachable) { to->reachable = 1; F(state_set_push(worklist, to)); } } } for (struct state *s = fa->initial; s->next != NULL; ) { if (s->next->hash == 0 && s->next->tused == 0) { struct state *del = s->next; s->next = del->next; free(del->trans); free(del); } else { s = s->next; } } result = 0; error: state_set_free(worklist); return result; } /* Convert an FA to a regular expression. * The strategy is the following: * (1) For all states S1 and S2, convert the transitions between them * into one transition whose regexp is a CSET * (2) Add a new initial state INI and a new final state FIN to the automaton, * a transition from INI to the old initial state of FA, and a transition * from all accepting states of FA to FIN; the regexp on those transitions * matches only the empty word * (3) Eliminate states S (except for INI and FIN) one by one: * Let LOOP the regexp for the transition S -> S if it exists, epsilon * otherwise. * For all S1, S2 different from S with S1 -> S -> S2 * Let R1 the regexp of S1 -> S * R2 the regexp of S -> S2 * R3 the regexp S1 -> S2 (or epsilon if no such transition) * set the regexp on the transition S1 -> S2 to * R1 . (LOOP)* . R2 | R3 * (4) The regexp for the whole FA can now be found as the regexp of * the transition INI -> FIN * (5) Convert that STRUCT RE to a string with RE_AS_STRING */ int fa_as_regexp(struct fa *fa, char **regexp, size_t *regexp_len) { int r; struct state *fin = NULL, *ini = NULL; struct re *eps = NULL; *regexp = NULL; *regexp_len = 0; fa = fa_clone(fa); if (fa == NULL) goto error; eps = make_re(EPSILON); if (eps == NULL) goto error; fin = add_state(fa,1); if (fin == NULL) goto error; fa->trans_re = 1; list_for_each(s, fa->initial) { r = convert_trans_to_re(s); if (r < 0) goto error; if (s->accept && s != fin) { r = add_new_re_trans(s, fin, ref(eps)); if (r < 0) goto error; s->accept = 0; } } ini = add_state(fa, 0); if (ini == NULL) goto error; r = add_new_re_trans(ini, fa->initial, ref(eps)); if (r < 0) goto error; set_initial(fa, ini); convert_strings(fa); list_for_each(s, fa->initial->next) { if (s == fin) continue; /* Eliminate S */ struct re *loop = eps; for_each_trans(t, s) { if (t->to == s) loop = t->re; } list_for_each(s1, fa->initial) { if (s == s1) continue; for (int t1 = 0; t1 < s1->tused; t1++) { if (s1->trans[t1].to == s) { for (int t = 0; t < s->tused; t++) { if (s->trans[t].to == s) continue; r = re_collapse_trans(s1, s->trans[t].to, s1->trans[t1].re, loop, s->trans[t].re); if (r < 0) goto error; } } } } } re_unref(eps); for_each_trans(t, fa->initial) { if (t->to == fin) { struct re_str str; MEMZERO(&str, 1); if (re_as_string(t->re, &str) < 0) goto error; *regexp = str.rx; *regexp_len = str.len; } } list_for_each(s, fa->initial) { for_each_trans(t, s) { unref(t->re, re); } } fa_free(fa); return 0; error: fa_free(fa); re_unref(eps); return -1; } static int re_restrict_alphabet(struct re *re, uchar from, uchar to) { int r1, r2; int result = 0; switch(re->type) { case UNION: case CONCAT: r1 = re_restrict_alphabet(re->exp1, from, to); r2 = re_restrict_alphabet(re->exp2, from, to); result = (r1 != 0) ? r1 : r2; break; case CSET: if (re->negate) { re->negate = 0; bitset_negate(re->cset, UCHAR_NUM); } for (int i=from; i <= to; i++) bitset_clr(re->cset, i); break; case CHAR: if (from <= re->c && re->c <= to) result = -1; break; case ITER: result = re_restrict_alphabet(re->exp, from, to); break; case EPSILON: break; default: assert(0); abort(); break; } return result; } int fa_restrict_alphabet(const char *regexp, size_t regexp_len, char **newregexp, size_t *newregexp_len, char from, char to) { int result; struct re *re = NULL; struct re_parse parse; struct re_str str; *newregexp = NULL; MEMZERO(&parse, 1); parse.rx = regexp; parse.rend = regexp + regexp_len; parse.error = REG_NOERROR; re = parse_regexp(&parse); if (parse.error != REG_NOERROR) return parse.error; result = re_restrict_alphabet(re, from, to); if (result != 0) { result = -2; goto done; } MEMZERO(&str, 1); result = re_as_string(re, &str); *newregexp = str.rx; *newregexp_len = str.len; done: re_unref(re); return result; } int fa_expand_char_ranges(const char *regexp, size_t regexp_len, char **newregexp, size_t *newregexp_len) { int result; struct re *re = NULL; struct re_parse parse; struct re_str str; *newregexp = NULL; MEMZERO(&parse, 1); parse.rx = regexp; parse.rend = regexp + regexp_len; parse.error = REG_NOERROR; parse.no_ranges = 1; re = parse_regexp(&parse); if (parse.error != REG_NOERROR) return parse.error; MEMZERO(&str, 1); result = re_as_string(re, &str); *newregexp = str.rx; *newregexp_len = str.len; re_unref(re); return result; } /* Expand regexp so that it is case-insensitive in a case-sensitive match. * * Return 1 when a change was made, -1 when an allocation failed, and 0 * when no change was made. */ static int re_case_expand(struct re *re) { int result = 0, r1, r2; switch(re->type) { case UNION: case CONCAT: r1 = re_case_expand(re->exp1); r2 = re_case_expand(re->exp2); result = (r1 != 0) ? r1 : r2; break; case CSET: for (int c = 'A'; c <= 'Z'; c++) if (bitset_get(re->cset, c)) { result = 1; bitset_set(re->cset, tolower(c)); } for (int c = 'a'; c <= 'z'; c++) if (bitset_get(re->cset, c)) { result = 1; bitset_set(re->cset, toupper(c)); } break; case CHAR: if (isalpha(re->c)) { int c = re->c; re->type = CSET; re->negate = false; re->no_ranges = 0; re->cset = bitset_init(UCHAR_NUM); if (re->cset == NULL) return -1; bitset_set(re->cset, tolower(c)); bitset_set(re->cset, toupper(c)); result = 1; } break; case ITER: result = re_case_expand(re->exp); break; case EPSILON: break; default: assert(0); abort(); break; } return result; } int fa_expand_nocase(const char *regexp, size_t regexp_len, char **newregexp, size_t *newregexp_len) { int result, r; struct re *re = NULL; struct re_parse parse; struct re_str str; *newregexp = NULL; MEMZERO(&parse, 1); parse.rx = regexp; parse.rend = regexp + regexp_len; parse.error = REG_NOERROR; re = parse_regexp(&parse); if (parse.error != REG_NOERROR) return parse.error; r = re_case_expand(re); if (r < 0) { re_unref(re); return REG_ESPACE; } if (r == 1) { MEMZERO(&str, 1); result = re_as_string(re, &str); *newregexp = str.rx; *newregexp_len = str.len; } else { *newregexp = strndup(regexp, regexp_len); *newregexp_len = regexp_len; result = (*newregexp == NULL) ? REG_ESPACE : REG_NOERROR; } re_unref(re); return result; } static void print_char(FILE *out, uchar c) { /* We escape '/' as '\\/' since dot chokes on bare slashes in labels; Also, a space ' ' is shown as '\s' */ static const char *const escape_from = " \n\t\v\b\r\f\a/\0"; static const char *const escape_to = "sntvbrfa/0"; char *p = strchr(escape_from, c); if (p != NULL) { int i = p - escape_from; fprintf(out, "\\\\%c", escape_to[i]); } else if (! isprint(c)) { fprintf(out, "\\\\0%03o", (unsigned char) c); } else if (c == '"') { fprintf(out, "\\\""); } else { fputc(c, out); } } void fa_dot(FILE *out, struct fa *fa) { fprintf(out, "digraph {\n rankdir=LR;"); list_for_each(s, fa->initial) { if (s->accept) { fprintf(out, "\"%p\" [shape=doublecircle];\n", s); } else { fprintf(out, "\"%p\" [shape=circle];\n", s); } } fprintf(out, "%s -> \"%p\";\n", fa->deterministic ? "dfa" : "nfa", fa->initial); struct re_str str; MEMZERO(&str, 1); list_for_each(s, fa->initial) { for_each_trans(t, s) { fprintf(out, "\"%p\" -> \"%p\" [ label = \"", s, t->to); if (fa->trans_re) { re_as_string(t->re, &str); for (int i=0; i < str.len; i++) { print_char(out, str.rx[i]); } release_re_str(&str); } else { print_char(out, t->min); if (t->min != t->max) { fputc('-', out); print_char(out, t->max); } } fprintf(out, "\" ];\n"); } } fprintf(out, "}\n"); } int fa_json(FILE *out, struct fa *fa) { hash_val_t *list_hashes = NULL; int list_size = 100; int num_states = 0; int it; char first = true; int result = -1; fprintf(out,"{\n\t\"final\": ["); F(ALLOC_N(list_hashes, list_size)); list_for_each(s, fa->initial) { if (num_states == list_size - 1){ list_size += list_size; F(REALLOC_N(list_hashes, list_size)); } // Store hash value list_hashes[num_states] = s->hash; // We use the hashes to map states to Z_{num_states} s->hash = num_states++; if (s->accept) { if (first) { fprintf(out,"%ld", s->hash); first = false; } else { fprintf(out, ", %ld", s->hash); } } } fprintf(out, "],\n\t\"deterministic\": %d,\n\t\"transitions\": [\n", fa->deterministic ? 1 : 0); first = true; list_for_each(s, fa->initial) { for_each_trans(t, s) { if (!first) fprintf(out, ",\n"); first = false; fprintf(out, "\t\t{ \"from\": %ld, \"to\": %ld, \"on\": \"", s->hash, t->to->hash); print_char(out, t->min); if (t->min != t->max) { fputc('-', out); print_char(out, t->max); } fprintf(out, "\" }"); } } fprintf(out,"\n\t]\n}"); result = 0; error: // Restoring hash values to leave the FA structure untouched. That is // only needed if we actually copied hashes, indicated by num_states // being non-zero if (num_states > 0) { it = 0; list_for_each(s, fa->initial) { s->hash = list_hashes[it++]; } } free(list_hashes); return result; } bool fa_is_deterministic(struct fa *fa) { return fa->deterministic; } struct state *fa_state_initial(struct fa *fa) { return fa->initial; } bool fa_state_is_accepting(struct state *st) { return st->accept; } struct state* fa_state_next(struct state *st) { return st->next; } size_t fa_state_num_trans(struct state *st) { return st->tused; } int fa_state_trans(struct state *st, size_t i, struct state **to, unsigned char *min, unsigned char *max) { if (st->tused <= i) return -1; (*to) = st->trans[i].to; (*min) = st->trans[i].min; (*max) = st->trans[i].max; return 0; } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/lexer.l0000644000175000017500000001300014161102026012030 00000000000000/* Scanner for config specs -*- C -*- */ %option 8bit never-interactive yylineno %option bison-bridge bison-locations %option reentrant noyywrap %option warn nodefault %option outfile="lex.yy.c" prefix="augl_" %option noinput nounput %top{ /* config.h must precede flex's inclusion of in order for its _GNU_SOURCE definition to take effect. */ #include } %{ #include "syntax.h" #include "errcode.h" typedef struct info YYLTYPE; #define YYLTYPE_IS_DECLARED 1 #include "parser.h" /* Advance of NUM lines. */ # define LOCATION_LINES(Loc, Num) \ (Loc).last_column = 0; \ (Loc).last_line += Num; /* Restart: move the first cursor to the last position. */ # define LOCATION_STEP(Loc) \ (Loc).first_column = (Loc).last_column; \ (Loc).first_line = (Loc).last_line; /* The lack of reference counting for filename is intentional */ #define YY_USER_ACTION \ do { \ yylloc->last_column += yyleng; \ yylloc->filename = augl_get_info(yyscanner)->filename; \ yylloc->error = augl_get_info(yyscanner)->error; \ } while(0); #define YY_USER_INIT LOCATION_STEP(*yylloc) #define YY_EXTRA_TYPE struct state * int augl_init_lexer(struct state *state, yyscan_t * scanner); void augl_close_lexer(yyscan_t *scanner); struct info *augl_get_info(yyscan_t yyscanner); static void loc_update(YYLTYPE *yylloc, const char *s, int len) { for (int i=0; i < len; i++) { if (s[i] == '\n') { LOCATION_LINES(*yylloc, 1); } } } static char *regexp_literal(const char *s, int len) { char *u = unescape(s, len, RX_ESCAPES); if (u == NULL) return NULL; size_t u_len = strlen(u); regexp_c_locale(&u, &u_len); return u; } %} DIGIT [0-9] UID [A-Z][A-Za-z0-9_]* LID [a-z_][A-Za-z0-9_]* LETREC let[ \t]+rec WS [ \t\n] QID {UID}\.{LID} ARROW -> %s COMMENT %% <*> { [ \t]* LOCATION_STEP(*yylloc); \n+ LOCATION_LINES(*yylloc, yyleng); LOCATION_STEP(*yylloc); (\r\n)+ LOCATION_LINES(*yylloc, yyleng/2); LOCATION_STEP(*yylloc); } { \"([^\\\"]|\\(.|\n))*\" { loc_update(yylloc, yytext, yyleng); yylval->string = unescape(yytext+1, yyleng-2, STR_ESCAPES); return DQUOTED; } \/([^\\\/]|\\(.|\n))*\/i { loc_update(yylloc, yytext, yyleng); yylval->regexp.nocase = 1; yylval->regexp.pattern = regexp_literal(yytext+1, yyleng-3); return REGEXP; } \/([^\\\/]|\\(.|\n))*\/ { loc_update(yylloc, yytext, yyleng); yylval->regexp.nocase = 0; yylval->regexp.pattern = regexp_literal(yytext+1, yyleng-2); return REGEXP; } [|*?+()=:;\.\[\]{}-] return yytext[0]; module return KW_MODULE; {LETREC}/{WS} return KW_LET_REC; let return KW_LET; string return KW_STRING; regexp return KW_REGEXP; lens return KW_LENS; in return KW_IN; autoload return KW_AUTOLOAD; /* tests */ test return KW_TEST; get return KW_GET; put return KW_PUT; after return KW_AFTER; {ARROW} return ARROW; {QID} { yylval->string = strndup(yytext, yyleng); return QIDENT; } {LID} { yylval->string = strndup(yytext, yyleng); return LIDENT; } {UID} { yylval->string = strndup(yytext, yyleng); return UIDENT; } \(\* { augl_get_extra(yyscanner)->comment_depth = 1; BEGIN(COMMENT); } . { report_error(augl_get_info(yyscanner)->error, AUG_ESYNTAX, "%s:%d:%d: Unexpected character %c", augl_get_info(yyscanner)->filename->str, yylineno, yylloc->first_column, yytext[0]); } <> { augl_close_lexer(yyscanner); yyterminate(); } } { \(\* { augl_get_extra(yyscanner)->comment_depth += 1; } \*\) { augl_get_extra(yyscanner)->comment_depth -= 1; if (augl_get_extra(yyscanner)->comment_depth == 0) BEGIN(INITIAL); } . /* Skip */; <> { report_error(augl_get_info(yyscanner)->error, AUG_ESYNTAX, "%s:%d:%d: Missing *)", augl_get_info(yyscanner)->filename->str, yylineno, yylloc->first_column); augl_close_lexer(yyscanner); yyterminate(); } } %% void augl_close_lexer(yyscan_t *scanner) { FILE *fp = augl_get_in(scanner); if (fp != NULL) { fclose(fp); augl_set_in(NULL, scanner); } } int augl_init_lexer(struct state *state, yyscan_t *scanner) { FILE *f; struct string *name = state->info->filename; f = fopen(name->str, "r"); if (f == NULL) return -1; if (augl_lex_init(scanner) != 0) { fclose(f); return -1; } augl_set_extra(state, *scanner); augl_set_in(f, *scanner); return 0; } struct info *augl_get_info(yyscan_t scanner) { return augl_get_extra(scanner)->info; } augeas-1.13.0/src/parser.h0000644000175000017500000001104514161102722012213 00000000000000/* A Bison parser, made by GNU Bison 3.8.2. */ /* Bison interface for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, especially those whose name start with YY_ or yy_. They are private implementation details that can be changed or removed. */ #ifndef YY_AUGL_PARSER_H_INCLUDED # define YY_AUGL_PARSER_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int augl_debug; #endif /* Token kinds. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { YYEMPTY = -2, YYEOF = 0, /* "end of file" */ YYerror = 256, /* error */ YYUNDEF = 257, /* "invalid token" */ DQUOTED = 258, /* DQUOTED */ REGEXP = 259, /* REGEXP */ LIDENT = 260, /* LIDENT */ UIDENT = 261, /* UIDENT */ QIDENT = 262, /* QIDENT */ ARROW = 263, /* ARROW */ KW_MODULE = 264, /* KW_MODULE */ KW_AUTOLOAD = 265, /* KW_AUTOLOAD */ KW_LET = 266, /* KW_LET */ KW_LET_REC = 267, /* KW_LET_REC */ KW_IN = 268, /* KW_IN */ KW_STRING = 269, /* KW_STRING */ KW_REGEXP = 270, /* KW_REGEXP */ KW_LENS = 271, /* KW_LENS */ KW_TEST = 272, /* KW_TEST */ KW_GET = 273, /* KW_GET */ KW_PUT = 274, /* KW_PUT */ KW_AFTER = 275 /* KW_AFTER */ }; typedef enum yytokentype yytoken_kind_t; #endif /* Token kinds. */ #define YYEMPTY -2 #define YYEOF 0 #define YYerror 256 #define YYUNDEF 257 #define DQUOTED 258 #define REGEXP 259 #define LIDENT 260 #define UIDENT 261 #define QIDENT 262 #define ARROW 263 #define KW_MODULE 264 #define KW_AUTOLOAD 265 #define KW_LET 266 #define KW_LET_REC 267 #define KW_IN 268 #define KW_STRING 269 #define KW_REGEXP 270 #define KW_LENS 271 #define KW_TEST 272 #define KW_GET 273 #define KW_PUT 274 #define KW_AFTER 275 /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 89 "parser.y" struct term *term; struct type *type; struct ident *ident; struct tree *tree; char *string; struct { int nocase; char *pattern; } regexp; int intval; enum quant_tag quant; #line 121 "parser.h" }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif /* Location type. */ #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE YYLTYPE; struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; }; # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif int augl_parse (struct term **term, yyscan_t scanner); /* "%code provides" blocks. */ #line 46 "parser.y" #include "info.h" /* Track custom scanner state */ struct state { struct info *info; unsigned int comment_depth; }; #line 160 "parser.h" #endif /* !YY_AUGL_PARSER_H_INCLUDED */ augeas-1.13.0/src/hash.c0000644000175000017500000007010414161102026011633 00000000000000/* * Hash Table Data Type * Copyright (C) 1997 Kaz Kylheku * * Free Software License: * * All rights are reserved by the author, with the following exceptions: * Permission is granted to freely reproduce and distribute this software, * possibly in exchange for a fee, provided that this copyright notice appears * intact. Permission is also granted to adapt this software to produce * derivative works, as long as the modified versions carry this copyright * notice and additional notices stating that the work has been modified. * This source code may be translated into executable form and incorporated * into proprietary software; there is no requirement for such software to * contain a copyright notice related to this source. * * $Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $ * $Name: kazlib_1_20 $ * * 2008-04-17 Small modifications to build with stricter warnings * David Lutterkort * */ #include #include #include #include #include #define HASH_IMPLEMENTATION #include "internal.h" #include "hash.h" #ifdef HASH_DEBUG_VERIFY # define expensive_assert(expr) assert (expr) #else # define expensive_assert(expr) /* empty */ #endif #ifdef KAZLIB_RCSID static const char rcsid[] = "$Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $"; #endif #define INIT_BITS 4 #define INIT_SIZE (1UL << (INIT_BITS)) /* must be power of two */ #define INIT_MASK ((INIT_SIZE) - 1) #define next hash_next #define key hash_key #define data hash_data #define hkey hash_hkey #define table hash_table #define nchains hash_nchains #define nodecount hash_nodecount #define maxcount hash_maxcount #define highmark hash_highmark #define lowmark hash_lowmark #define compare hash_compare #define function hash_function #define allocnode hash_allocnode #define freenode hash_freenode #define context hash_context #define mask hash_mask #define dynamic hash_dynamic #define table hash_table #define chain hash_chain static hnode_t *hnode_alloc(void *context); static void hnode_free(hnode_t *node, void *context); static hash_val_t hash_fun_default(const void *key); static int hash_comp_default(const void *key1, const void *key2); int hash_val_t_bit; /* * Compute the number of bits in the hash_val_t type. We know that hash_val_t * is an unsigned integral type. Thus the highest value it can hold is a * Mersenne number (power of two, less one). We initialize a hash_val_t * object with this value and then shift bits out one by one while counting. * Notes: * 1. HASH_VAL_T_MAX is a Mersenne number---one that is one less than a power * of two. This means that its binary representation consists of all one * bits, and hence ``val'' is initialized to all one bits. * 2. While bits remain in val, we increment the bit count and shift it to the * right, replacing the topmost bit by zero. */ static void compute_bits(void) { hash_val_t val = HASH_VAL_T_MAX; /* 1 */ int bits = 0; while (val) { /* 2 */ bits++; val >>= 1; } hash_val_t_bit = bits; } /* * Verify whether the given argument is a power of two. */ static int is_power_of_two(hash_val_t arg) { if (arg == 0) return 0; while ((arg & 1) == 0) arg >>= 1; return (arg == 1); } /* * Compute a shift amount from a given table size */ static hash_val_t compute_mask(hashcount_t size) { assert (is_power_of_two(size)); assert (size >= 2); return size - 1; } /* * Initialize the table of pointers to null. */ static void clear_table(hash_t *hash) { hash_val_t i; for (i = 0; i < hash->nchains; i++) hash->table[i] = NULL; } /* * Double the size of a dynamic table. This works as follows. Each chain splits * into two adjacent chains. The shift amount increases by one, exposing an * additional bit of each hashed key. For each node in the original chain, the * value of this newly exposed bit will decide which of the two new chains will * receive the node: if the bit is 1, the chain with the higher index will have * the node, otherwise the lower chain will receive the node. In this manner, * the hash table will continue to function exactly as before without having to * rehash any of the keys. * Notes: * 1. Overflow check. * 2. The new number of chains is twice the old number of chains. * 3. The new mask is one bit wider than the previous, revealing a * new bit in all hashed keys. * 4. Allocate a new table of chain pointers that is twice as large as the * previous one. * 5. If the reallocation was successful, we perform the rest of the growth * algorithm, otherwise we do nothing. * 6. The exposed_bit variable holds a mask with which each hashed key can be * AND-ed to test the value of its newly exposed bit. * 7. Now loop over each chain in the table and sort its nodes into two * chains based on the value of each node's newly exposed hash bit. * 8. The low chain replaces the current chain. The high chain goes * into the corresponding sister chain in the upper half of the table. * 9. We have finished dealing with the chains and nodes. We now update * the various bookeeping fields of the hash structure. */ static void grow_table(hash_t *hash) { hnode_t **newtable; assert (2 * hash->nchains > hash->nchains); /* 1 */ newtable = realloc(hash->table, sizeof *newtable * hash->nchains * 2); /* 4 */ if (newtable) { /* 5 */ hash_val_t mask = (hash->mask << 1) | 1; /* 3 */ hash_val_t exposed_bit = mask ^ hash->mask; /* 6 */ hash_val_t chain; assert (mask != hash->mask); for (chain = 0; chain < hash->nchains; chain++) { /* 7 */ hnode_t *low_chain = 0, *high_chain = 0, *hptr, *next; for (hptr = newtable[chain]; hptr != 0; hptr = next) { next = hptr->next; if (hptr->hkey & exposed_bit) { hptr->next = high_chain; high_chain = hptr; } else { hptr->next = low_chain; low_chain = hptr; } } newtable[chain] = low_chain; /* 8 */ newtable[chain + hash->nchains] = high_chain; } hash->table = newtable; /* 9 */ hash->mask = mask; hash->nchains *= 2; hash->lowmark *= 2; hash->highmark *= 2; } expensive_assert (hash_verify(hash)); } /* * Cut a table size in half. This is done by folding together adjacent chains * and populating the lower half of the table with these chains. The chains are * simply spliced together. Once this is done, the whole table is reallocated * to a smaller object. * Notes: * 1. It is illegal to have a hash table with one slot. This would mean that * hash->shift is equal to hash_val_t_bit, an illegal shift value. * Also, other things could go wrong, such as hash->lowmark becoming zero. * 2. Looping over each pair of sister chains, the low_chain is set to * point to the head node of the chain in the lower half of the table, * and high_chain points to the head node of the sister in the upper half. * 3. The intent here is to compute a pointer to the last node of the * lower chain into the low_tail variable. If this chain is empty, * low_tail ends up with a null value. * 4. If the lower chain is not empty, we simply tack the upper chain onto it. * If the upper chain is a null pointer, nothing happens. * 5. Otherwise if the lower chain is empty but the upper one is not, * If the low chain is empty, but the high chain is not, then the * high chain is simply transferred to the lower half of the table. * 6. Otherwise if both chains are empty, there is nothing to do. * 7. All the chain pointers are in the lower half of the table now, so * we reallocate it to a smaller object. This, of course, invalidates * all pointer-to-pointers which reference into the table from the * first node of each chain. * 8. Though it's unlikely, the reallocation may fail. In this case we * pretend that the table _was_ reallocated to a smaller object. * 9. Finally, update the various table parameters to reflect the new size. */ static void shrink_table(hash_t *hash) { hash_val_t chain, nchains; hnode_t **newtable, *low_tail, *low_chain, *high_chain; assert (hash->nchains >= 2); /* 1 */ nchains = hash->nchains / 2; for (chain = 0; chain < nchains; chain++) { low_chain = hash->table[chain]; /* 2 */ high_chain = hash->table[chain + nchains]; for (low_tail = low_chain; low_tail && low_tail->next; low_tail = low_tail->next) ; /* 3 */ if (low_chain != 0) /* 4 */ low_tail->next = high_chain; else if (high_chain != 0) /* 5 */ hash->table[chain] = high_chain; else assert (hash->table[chain] == NULL); /* 6 */ } newtable = realloc(hash->table, sizeof *newtable * nchains); /* 7 */ if (newtable) /* 8 */ hash->table = newtable; hash->mask >>= 1; /* 9 */ hash->nchains = nchains; hash->lowmark /= 2; hash->highmark /= 2; expensive_assert (hash_verify(hash)); } /* * Create a dynamic hash table. Both the hash table structure and the table * itself are dynamically allocated. Furthermore, the table is extendible in * that it will automatically grow as its load factor increases beyond a * certain threshold. * Notes: * 1. If the number of bits in the hash_val_t type has not been computed yet, * we do so here, because this is likely to be the first function that the * user calls. * 2. Allocate a hash table control structure. * 3. If a hash table control structure is successfully allocated, we * proceed to initialize it. Otherwise we return a null pointer. * 4. We try to allocate the table of hash chains. * 5. If we were able to allocate the hash chain table, we can finish * initializing the hash structure and the table. Otherwise, we must * backtrack by freeing the hash structure. * 6. INIT_SIZE should be a power of two. The high and low marks are always set * to be twice the table size and half the table size respectively. When the * number of nodes in the table grows beyond the high size (beyond load * factor 2), it will double in size to cut the load factor down to about * about 1. If the table shrinks down to or beneath load factor 0.5, * it will shrink, bringing the load up to about 1. However, the table * will never shrink beneath INIT_SIZE even if it's emptied. * 7. This indicates that the table is dynamically allocated and dynamically * resized on the fly. A table that has this value set to zero is * assumed to be statically allocated and will not be resized. * 8. The table of chains must be properly reset to all null pointers. */ hash_t *hash_create(hashcount_t maxcount, hash_comp_t compfun, hash_fun_t hashfun) { hash_t *hash; if (hash_val_t_bit == 0) /* 1 */ compute_bits(); hash = malloc(sizeof *hash); /* 2 */ if (hash) { /* 3 */ hash->table = malloc(sizeof *hash->table * INIT_SIZE); /* 4 */ if (hash->table) { /* 5 */ hash->nchains = INIT_SIZE; /* 6 */ hash->highmark = INIT_SIZE * 2; hash->lowmark = INIT_SIZE / 2; hash->nodecount = 0; hash->maxcount = maxcount; hash->compare = compfun ? compfun : hash_comp_default; hash->function = hashfun ? hashfun : hash_fun_default; hash->allocnode = hnode_alloc; hash->freenode = hnode_free; hash->context = NULL; hash->mask = INIT_MASK; hash->dynamic = 1; /* 7 */ clear_table(hash); /* 8 */ expensive_assert (hash_verify(hash)); return hash; } free(hash); } return NULL; } /* * Select a different set of node allocator routines. */ void hash_set_allocator(hash_t *hash, hnode_alloc_t al, hnode_free_t fr, void *context) { assert (hash_count(hash) == 0); hash->allocnode = al ? al : hnode_alloc; hash->freenode = fr ? fr : hnode_free; hash->context = context; } /* * Free every node in the hash using the hash->freenode() function pointer, and * cause the hash to become empty. */ void hash_free_nodes(hash_t *hash) { hnode_t *node, *next; hash_val_t chain; for (chain = 0; chain < hash->nchains; chain ++) { node = hash->table[chain]; while (node != NULL) { next = node->next; hash->freenode(node, hash->context); node = next; } hash->table[chain] = NULL; } hash->nodecount = 0; clear_table(hash); } /* * Obsolescent function for removing all nodes from a table, * freeing them and then freeing the table all in one step. */ void hash_free(hash_t *hash) { #ifdef KAZLIB_OBSOLESCENT_DEBUG assert ("call to obsolescent function hash_free()" && 0); #endif hash_free_nodes(hash); hash_destroy(hash); } /* * Free a dynamic hash table structure. */ void hash_destroy(hash_t *hash) { assert (hash_val_t_bit != 0); assert (hash_isempty(hash)); free(hash->table); free(hash); } /* * Initialize a user supplied hash structure. The user also supplies a table of * chains which is assigned to the hash structure. The table is static---it * will not grow or shrink. * 1. See note 1. in hash_create(). * 2. The user supplied array of pointers hopefully contains nchains nodes. * 3. See note 7. in hash_create(). * 4. We must dynamically compute the mask from the given power of two table * size. * 5. The user supplied table can't be assumed to contain null pointers, * so we reset it here. */ hash_t *hash_init(hash_t *hash, hashcount_t maxcount, hash_comp_t compfun, hash_fun_t hashfun, hnode_t **table, hashcount_t nchains) { if (hash_val_t_bit == 0) /* 1 */ compute_bits(); assert (is_power_of_two(nchains)); hash->table = table; /* 2 */ hash->nchains = nchains; hash->nodecount = 0; hash->maxcount = maxcount; hash->compare = compfun ? compfun : hash_comp_default; hash->function = hashfun ? hashfun : hash_fun_default; hash->dynamic = 0; /* 3 */ hash->mask = compute_mask(nchains); /* 4 */ clear_table(hash); /* 5 */ expensive_assert (hash_verify(hash)); return hash; } /* * Reset the hash scanner so that the next element retrieved by * hash_scan_next() shall be the first element on the first non-empty chain. * Notes: * 1. Locate the first non empty chain. * 2. If an empty chain is found, remember which one it is and set the next * pointer to refer to its first element. * 3. Otherwise if a chain is not found, set the next pointer to NULL * so that hash_scan_next() shall indicate failure. */ void hash_scan_begin(hscan_t *scan, hash_t *hash) { hash_val_t nchains = hash->nchains; hash_val_t chain; scan->table = hash; /* 1 */ for (chain = 0; chain < nchains && hash->table[chain] == 0; chain++) ; if (chain < nchains) { /* 2 */ scan->chain = chain; scan->next = hash->table[chain]; } else { /* 3 */ scan->next = NULL; } } /* * Retrieve the next node from the hash table, and update the pointer * for the next invocation of hash_scan_next(). * Notes: * 1. Remember the next pointer in a temporary value so that it can be * returned. * 2. This assertion essentially checks whether the module has been properly * initialized. The first point of interaction with the module should be * either hash_create() or hash_init(), both of which set hash_val_t_bit to * a non zero value. * 3. If the next pointer we are returning is not NULL, then the user is * allowed to call hash_scan_next() again. We prepare the new next pointer * for that call right now. That way the user is allowed to delete the node * we are about to return, since we will no longer be needing it to locate * the next node. * 4. If there is a next node in the chain (next->next), then that becomes the * new next node, otherwise ... * 5. We have exhausted the current chain, and must locate the next subsequent * non-empty chain in the table. * 6. If a non-empty chain is found, the first element of that chain becomes * the new next node. Otherwise there is no new next node and we set the * pointer to NULL so that the next time hash_scan_next() is called, a null * pointer shall be immediately returned. */ hnode_t *hash_scan_next(hscan_t *scan) { hnode_t *next = scan->next; /* 1 */ hash_t *hash = scan->table; hash_val_t chain = scan->chain + 1; hash_val_t nchains = hash->nchains; assert (hash_val_t_bit != 0); /* 2 */ if (next) { /* 3 */ if (next->next) { /* 4 */ scan->next = next->next; } else { while (chain < nchains && hash->table[chain] == 0) /* 5 */ chain++; if (chain < nchains) { /* 6 */ scan->chain = chain; scan->next = hash->table[chain]; } else { scan->next = NULL; } } } return next; } /* * Insert a node into the hash table. * Notes: * 1. It's illegal to insert more than the maximum number of nodes. The client * should verify that the hash table is not full before attempting an * insertion. * 2. The same key may not be inserted into a table twice. * 3. If the table is dynamic and the load factor is already at >= 2, * grow the table. * 4. We take the bottom N bits of the hash value to derive the chain index, * where N is the base 2 logarithm of the size of the hash table. */ void hash_insert(hash_t *hash, hnode_t *node, const void *key) { hash_val_t hkey, chain; assert (hash_val_t_bit != 0); assert (node->next == NULL); assert (hash->nodecount < hash->maxcount); /* 1 */ expensive_assert (hash_lookup(hash, key) == NULL); /* 2 */ if (hash->dynamic && hash->nodecount >= hash->highmark) /* 3 */ grow_table(hash); hkey = hash->function(key); chain = hkey & hash->mask; /* 4 */ node->key = key; node->hkey = hkey; node->next = hash->table[chain]; hash->table[chain] = node; hash->nodecount++; expensive_assert (hash_verify(hash)); } /* * Find a node in the hash table and return a pointer to it. * Notes: * 1. We hash the key and keep the entire hash value. As an optimization, when * we descend down the chain, we can compare hash values first and only if * hash values match do we perform a full key comparison. * 2. To locate the chain from among 2^N chains, we look at the lower N bits of * the hash value by anding them with the current mask. * 3. Looping through the chain, we compare the stored hash value inside each * node against our computed hash. If they match, then we do a full * comparison between the unhashed keys. If these match, we have located the * entry. */ hnode_t *hash_lookup(hash_t *hash, const void *key) { hash_val_t hkey, chain; hnode_t *nptr; hkey = hash->function(key); /* 1 */ chain = hkey & hash->mask; /* 2 */ for (nptr = hash->table[chain]; nptr; nptr = nptr->next) { /* 3 */ if (nptr->hkey == hkey && hash->compare(nptr->key, key) == 0) return nptr; } return NULL; } /* * Delete the given node from the hash table. Since the chains * are singly linked, we must locate the start of the node's chain * and traverse. * Notes: * 1. The node must belong to this hash table, and its key must not have * been tampered with. * 2. If this deletion will take the node count below the low mark, we * shrink the table now. * 3. Determine which chain the node belongs to, and fetch the pointer * to the first node in this chain. * 4. If the node being deleted is the first node in the chain, then * simply update the chain head pointer. * 5. Otherwise advance to the node's predecessor, and splice out * by updating the predecessor's next pointer. * 6. Indicate that the node is no longer in a hash table. */ hnode_t *hash_delete(hash_t *hash, hnode_t *node) { hash_val_t chain; hnode_t *hptr; expensive_assert (hash_lookup(hash, node->key) == node); /* 1 */ assert (hash_val_t_bit != 0); if (hash->dynamic && hash->nodecount <= hash->lowmark && hash->nodecount > INIT_SIZE) shrink_table(hash); /* 2 */ chain = node->hkey & hash->mask; /* 3 */ hptr = hash->table[chain]; if (hptr == node) { /* 4 */ hash->table[chain] = node->next; } else { while (hptr->next != node) { /* 5 */ assert (hptr != 0); hptr = hptr->next; } assert (hptr->next == node); hptr->next = node->next; } hash->nodecount--; expensive_assert (hash_verify(hash)); node->next = NULL; /* 6 */ return node; } int hash_alloc_insert(hash_t *hash, const void *key, void *data) { hnode_t *node = hash->allocnode(hash->context); if (node) { hnode_init(node, data); hash_insert(hash, node, key); return 0; } return -1; } void hash_delete_free(hash_t *hash, hnode_t *node) { hash_delete(hash, node); hash->freenode(node, hash->context); } /* * Exactly like hash_delete, except does not trigger table shrinkage. This is to be * used from within a hash table scan operation. See notes for hash_delete. */ hnode_t *hash_scan_delete(hash_t *hash, hnode_t *node) { hash_val_t chain; hnode_t *hptr; expensive_assert (hash_lookup(hash, node->key) == node); assert (hash_val_t_bit != 0); chain = node->hkey & hash->mask; hptr = hash->table[chain]; if (hptr == node) { hash->table[chain] = node->next; } else { while (hptr->next != node) hptr = hptr->next; hptr->next = node->next; } hash->nodecount--; expensive_assert (hash_verify(hash)); node->next = NULL; return node; } /* * Like hash_delete_free but based on hash_scan_delete. */ void hash_scan_delfree(hash_t *hash, hnode_t *node) { hash_scan_delete(hash, node); hash->freenode(node, hash->context); } /* * Verify whether the given object is a valid hash table. This means * Notes: * 1. If the hash table is dynamic, verify whether the high and * low expansion/shrinkage thresholds are powers of two. * 2. Count all nodes in the table, and test each hash value * to see whether it is correct for the node's chain. */ int hash_verify(hash_t *hash) { hashcount_t count = 0; hash_val_t chain; hnode_t *hptr; if (hash->dynamic) { /* 1 */ if (hash->lowmark >= hash->highmark) return 0; if (!is_power_of_two(hash->highmark)) return 0; if (!is_power_of_two(hash->lowmark)) return 0; } for (chain = 0; chain < hash->nchains; chain++) { /* 2 */ for (hptr = hash->table[chain]; hptr != 0; hptr = hptr->next) { if ((hptr->hkey & hash->mask) != chain) return 0; count++; } } if (count != hash->nodecount) return 0; return 1; } /* * Test whether the hash table is full and return 1 if this is true, * 0 if it is false. */ #undef hash_isfull int hash_isfull(hash_t *hash) { return hash->nodecount == hash->maxcount; } /* * Test whether the hash table is empty and return 1 if this is true, * 0 if it is false. */ #undef hash_isempty int hash_isempty(hash_t *hash) { return hash->nodecount == 0; } static hnode_t *hnode_alloc(ATTRIBUTE_UNUSED void *context) { return malloc(sizeof *hnode_alloc(NULL)); } static void hnode_free(hnode_t *node, ATTRIBUTE_UNUSED void *context) { free(node); } /* * Create a hash table node dynamically and assign it the given data. */ hnode_t *hnode_create(void *data) { hnode_t *node = malloc(sizeof *node); if (node) { node->data = data; node->next = NULL; } return node; } /* * Initialize a client-supplied node */ hnode_t *hnode_init(hnode_t *hnode, void *data) { hnode->data = data; hnode->next = NULL; return hnode; } /* * Destroy a dynamically allocated node. */ void hnode_destroy(hnode_t *hnode) { free(hnode); } #undef hnode_put void hnode_put(hnode_t *node, void *data) { node->data = data; } #undef hnode_get void *hnode_get(hnode_t *node) { return node->data; } #undef hnode_getkey const void *hnode_getkey(hnode_t *node) { return node->key; } #undef hash_count hashcount_t hash_count(hash_t *hash) { return hash->nodecount; } #undef hash_size hashcount_t hash_size(hash_t *hash) { return hash->nchains; } static hash_val_t hash_fun_default(const void *key) { static unsigned long randbox[] = { 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U, 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU, 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU, 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU, }; const unsigned char *str = key; hash_val_t acc = 0; while (*str) { acc ^= randbox[(*str + acc) & 0xf]; acc = (acc << 1) | (acc >> 31); acc &= 0xffffffffU; acc ^= randbox[((*str++ >> 4) + acc) & 0xf]; acc = (acc << 2) | (acc >> 30); acc &= 0xffffffffU; } return acc; } static int hash_comp_default(const void *key1, const void *key2) { return strcmp(key1, key2); } #ifdef KAZLIB_TEST_MAIN #include #include #include typedef char input_t[256]; static int tokenize(char *string, ...) { char **tokptr; va_list arglist; int tokcount = 0; va_start(arglist, string); tokptr = va_arg(arglist, char **); while (tokptr) { while (*string && isspace((unsigned char) *string)) string++; if (!*string) break; *tokptr = string; while (*string && !isspace((unsigned char) *string)) string++; tokptr = va_arg(arglist, char **); tokcount++; if (!*string) break; *string++ = 0; } va_end(arglist); return tokcount; } static char *dupstring(char *str) { int sz = strlen(str) + 1; char *new = malloc(sz); if (new) memcpy(new, str, sz); return new; } static hnode_t *new_node(void *c) { static hnode_t few[5]; static int count; if (count < 5) return few + count++; return NULL; } static void del_node(hnode_t *n, void *c) { } int main(void) { input_t in; hash_t *h = hash_create(HASHCOUNT_T_MAX, 0, 0); hnode_t *hn; hscan_t hs; char *tok1, *tok2, *val; const char *key; int prompt = 0; char *help = "a add value to hash table\n" "d delete value from hash table\n" "l lookup value in hash table\n" "n show size of hash table\n" "c show number of entries\n" "t dump whole hash table\n" "+ increase hash table (private func)\n" "- decrease hash table (private func)\n" "b print hash_t_bit value\n" "p turn prompt on\n" "s switch to non-functioning allocator\n" "q quit"; if (!h) puts("hash_create failed"); for (;;) { if (prompt) putchar('>'); fflush(stdout); if (!fgets(in, sizeof(input_t), stdin)) break; switch(in[0]) { case '?': puts(help); break; case 'b': printf("%d\n", hash_val_t_bit); break; case 'a': if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { puts("what?"); break; } key = dupstring(tok1); val = dupstring(tok2); if (!key || !val) { puts("out of memory"); free((void *) key); free(val); break; } if (hash_alloc_insert(h, key, val) < 0) { puts("hash_alloc_insert failed"); free((void *) key); free(val); break; } break; case 'd': if (tokenize(in+1, &tok1, (char **) 0) != 1) { puts("what?"); break; } hn = hash_lookup(h, tok1); if (!hn) { puts("hash_lookup failed"); break; } val = hnode_get(hn); key = hnode_getkey(hn); hash_scan_delfree(h, hn); free((void *) key); free(val); break; case 'l': if (tokenize(in+1, &tok1, (char **) 0) != 1) { puts("what?"); break; } hn = hash_lookup(h, tok1); if (!hn) { puts("hash_lookup failed"); break; } val = hnode_get(hn); puts(val); break; case 'n': printf("%lu\n", (unsigned long) hash_size(h)); break; case 'c': printf("%lu\n", (unsigned long) hash_count(h)); break; case 't': hash_scan_begin(&hs, h); while ((hn = hash_scan_next(&hs))) printf("%s\t%s\n", (char*) hnode_getkey(hn), (char*) hnode_get(hn)); break; case '+': grow_table(h); /* private function */ break; case '-': shrink_table(h); /* private function */ break; case 'q': exit(0); break; case '\0': break; case 'p': prompt = 1; break; case 's': hash_set_allocator(h, new_node, del_node, NULL); break; default: putchar('?'); putchar('\n'); break; } } return 0; } #endif augeas-1.13.0/src/parser.y0000644000175000017500000004624714161102026012245 00000000000000%{ #include #include "internal.h" #include "syntax.h" #include "list.h" #include "errcode.h" #include /* Work around a problem on FreeBSD where Bison looks for _STDLIB_H * to see if stdlib.h has been included, but the system includes * use _STDLIB_H_ */ #if HAVE_STDLIB_H && ! defined _STDLIB_H # include # define _STDLIB_H 1 #endif #define YYDEBUG 1 int augl_parse_file(struct augeas *aug, const char *name, struct term **term); typedef void *yyscan_t; typedef struct info YYLTYPE; #define YYLTYPE_IS_DECLARED 1 /* The lack of reference counting on filename is intentional */ # define YYLLOC_DEFAULT(Current, Rhs, N) \ do { \ (Current).filename = augl_get_info(scanner)->filename; \ (Current).error = augl_get_info(scanner)->error; \ if (N) { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } else { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ } while (0) %} %code provides { #include "info.h" /* Track custom scanner state */ struct state { struct info *info; unsigned int comment_depth; }; } %locations %error-verbose %name-prefix "augl_" %defines %pure-parser %parse-param {struct term **term} %parse-param {yyscan_t scanner} %lex-param {yyscan_t scanner} %initial-action { @$.first_line = 1; @$.first_column = 0; @$.last_line = 1; @$.last_column = 0; @$.filename = augl_get_info(scanner)->filename; @$.error = augl_get_info(scanner)->error; }; %token DQUOTED /* "foo" */ %token REGEXP /* /[ \t]+/ */ %token LIDENT UIDENT QIDENT %token ARROW /* Keywords */ %token KW_MODULE %token KW_AUTOLOAD %token KW_LET KW_LET_REC KW_IN %token KW_STRING %token KW_REGEXP %token KW_LENS %token KW_TEST KW_GET KW_PUT KW_AFTER %union { struct term *term; struct type *type; struct ident *ident; struct tree *tree; char *string; struct { int nocase; char *pattern; } regexp; int intval; enum quant_tag quant; } %type start decls %type exp composeexp unionexp minusexp catexp appexp rexp aexp %type param param_list %type qid id autoload %type type atype %type rep %type test_exp %type test_special_res %type tree_const tree_const2 tree_branch %type tree_label %{ /* Lexer */ extern int augl_lex (YYSTYPE * yylval_param,struct info * yylloc_param ,yyscan_t yyscanner); int augl_init_lexer(struct state *state, yyscan_t * scanner); void augl_close_lexer(yyscan_t *scanner); int augl_lex_destroy (yyscan_t yyscanner ); int augl_get_lineno (yyscan_t yyscanner ); int augl_get_column (yyscan_t yyscanner); struct info *augl_get_info(yyscan_t yyscanner); char *augl_get_text (yyscan_t yyscanner ); static void augl_error(struct info *locp, struct term **term, yyscan_t scanner, const char *s); /* TERM construction */ static struct info *clone_info(struct info *locp); static struct term *make_module(char *ident, char *autoload, struct term *decls, struct info *locp); static struct term *make_bind(char *ident, struct term *params, struct term *exp, struct term *decls, struct info *locp); static struct term *make_bind_rec(char *ident, struct term *exp, struct term *decls, struct info *locp); static struct term *make_let(char *ident, struct term *params, struct term *exp, struct term *body, struct info *locp); static struct term *make_binop(enum term_tag tag, struct term *left, struct term *right, struct info *locp); static struct term *make_unop(enum term_tag tag, struct term *exp, struct info *locp); static struct term *make_ident(char *qname, struct info *locp); static struct term *make_unit_term(struct info *locp); static struct term *make_string_term(char *value, struct info *locp); static struct term *make_regexp_term(char *pattern, int nocase, struct info *locp); static struct term *make_rep(struct term *exp, enum quant_tag quant, struct info *locp); static struct term *make_get_test(struct term *lens, struct term *arg, struct info *info); static struct term *make_put_test(struct term *lens, struct term *arg, struct term *cmds, struct info *info); static struct term *make_test(struct term *test, struct term *result, enum test_result_tag tr_tag, struct term *decls, struct info *locp); static struct term *make_tree_value(struct tree *, struct info*); static struct tree *tree_concat(struct tree *, struct tree *); #define LOC_MERGE(a, b, c) \ do { \ (a).filename = (b).filename; \ (a).first_line = (b).first_line; \ (a).first_column = (b).first_column; \ (a).last_line = (c).last_line; \ (a).last_column = (c).last_column; \ (a).error = (b).error; \ } while(0); %} %% start: KW_MODULE UIDENT '=' autoload decls { (*term) = make_module($2, $4, $5, &@1); } autoload: KW_AUTOLOAD LIDENT { $$ = $2; } | /* empty */ { $$ = NULL; } decls: KW_LET LIDENT param_list '=' exp decls { LOC_MERGE(@1, @1, @5); $$ = make_bind($2, $3, $5, $6, &@1); } | KW_LET_REC LIDENT '=' exp decls { LOC_MERGE(@1, @1, @4); $$ = make_bind_rec($2, $4, $5, &@1); } | KW_TEST test_exp '=' exp decls { LOC_MERGE(@1, @1, @4); $$ = make_test($2, $4, TR_CHECK, $5, &@1); } | KW_TEST test_exp '=' test_special_res decls { LOC_MERGE(@1, @1, @4); $$ = make_test($2, NULL, $4, $5, &@1); } | /* epsilon */ { $$ = NULL; } /* Test expressions and results */ test_exp: aexp KW_GET exp { $$ = make_get_test($1, $3, &@$); } | aexp KW_PUT aexp KW_AFTER exp { $$ = make_put_test($1, $3, $5, &@$); } test_special_res: '?' { $$ = TR_PRINT; } | '*' { $$ = TR_EXN; } /* General expressions */ exp: KW_LET LIDENT param_list '=' exp KW_IN exp { LOC_MERGE(@1, @1, @6); $$ = make_let($2, $3, $5, $7, &@1); } | composeexp composeexp: composeexp ';' unionexp { $$ = make_binop(A_COMPOSE, $1, $3, &@$); } | unionexp { $$ = $1; } unionexp: unionexp '|' minusexp { $$ = make_binop(A_UNION, $1, $3, &@$); } | minusexp { $$ = $1; } | tree_const { $$ = make_tree_value($1, &@1); } minusexp: minusexp '-' catexp { $$ = make_binop(A_MINUS, $1, $3, &@$); } | catexp { $$ = $1; } catexp: catexp '.' appexp { $$ = make_binop(A_CONCAT, $1, $3, &@$); } | appexp { $$ = $1; } appexp: appexp rexp { $$ = make_binop(A_APP, $1, $2, &@$); } | rexp { $$ = $1; } aexp: qid { $$ = make_ident($1, &@1); } | DQUOTED { $$ = make_string_term($1, &@1); } | REGEXP { $$ = make_regexp_term($1.pattern, $1.nocase, &@1); } | '(' exp ')' { $$ = $2; } | '[' exp ']' { $$ = make_unop(A_BRACKET, $2, &@$); } | '(' ')' { $$ = make_unit_term(&@$); } rexp: aexp rep { $$ = make_rep($1, $2, &@$); } | aexp { $$ = $1; } rep: '*' { $$ = Q_STAR; } | '+' { $$ = Q_PLUS; } | '?' { $$ = Q_MAYBE; } qid: LIDENT { $$ = $1; } | QIDENT { $$ = $1; } | KW_GET { $$ = strdup("get"); } | KW_PUT { $$ = strdup("put"); } param_list: param param_list { $$ = $2; list_cons($$, $1); } | /* epsilon */ { $$ = NULL; } param: '(' id ':' type ')' { $$ = make_param($2, $4, clone_info(&@1)); } id: LIDENT { $$ = $1; } | KW_GET { $$ = strdup("get"); } | KW_PUT { $$ = strdup("put"); } type: atype ARROW type { $$ = make_arrow_type($1, $3); } | atype { $$ = $1; } atype: KW_STRING { $$ = make_base_type(T_STRING); } | KW_REGEXP { $$ = make_base_type(T_REGEXP); } | KW_LENS { $$ = make_base_type(T_LENS); } | '(' type ')' { $$ = $2; } tree_const: tree_const '{' tree_branch '}' { $$ = tree_concat($1, $3); } | '{' tree_branch '}' { $$ = tree_concat($2, NULL); } tree_const2: tree_const2 '{' tree_branch '}' { $$ = tree_concat($1, $3); } | /* empty */ { $$ = NULL; } tree_branch: tree_label tree_const2 { $$ = make_tree($1, NULL, NULL, $2); } | tree_label '=' DQUOTED tree_const2 { $$ = make_tree($1, $3, NULL, $4); } tree_label: DQUOTED | /* empty */ { $$ = NULL; } %% int augl_parse_file(struct augeas *aug, const char *name, struct term **term) { yyscan_t scanner; struct state state; struct string *sname = NULL; struct info info; int result = -1; int r; *term = NULL; r = make_ref(sname); ERR_NOMEM(r < 0, aug); sname->str = strdup(name); ERR_NOMEM(sname->str == NULL, aug); MEMZERO(&info, 1); info.ref = UINT_MAX; info.filename = sname; info.error = aug->error; MEMZERO(&state, 1); state.info = &info; state.comment_depth = 0; if (augl_init_lexer(&state, &scanner) < 0) { augl_error(&info, term, NULL, "file not found"); goto error; } yydebug = getenv("YYDEBUG") != NULL; r = augl_parse(term, scanner); augl_close_lexer(scanner); augl_lex_destroy(scanner); if (r == 1) { augl_error(&info, term, NULL, "syntax error"); goto error; } else if (r == 2) { augl_error(&info, term, NULL, "parser ran out of memory"); ERR_NOMEM(1, aug); } result = 0; error: unref(sname, string); // free TERM return result; } // FIXME: Nothing here checks for alloc errors. static struct info *clone_info(struct info *locp) { struct info *info; make_ref(info); info->filename = ref(locp->filename); info->first_line = locp->first_line; info->first_column = locp->first_column; info->last_line = locp->last_line; info->last_column = locp->last_column; info->error = locp->error; return info; } static struct term *make_term_locp(enum term_tag tag, struct info *locp) { struct info *info = clone_info(locp); return make_term(tag, info); } static struct term *make_module(char *ident, char *autoload, struct term *decls, struct info *locp) { struct term *term = make_term_locp(A_MODULE, locp); term->mname = ident; term->autoload = autoload; term->decls = decls; return term; } static struct term *make_bind(char *ident, struct term *params, struct term *exp, struct term *decls, struct info *locp) { struct term *term = make_term_locp(A_BIND, locp); if (params != NULL) exp = build_func(params, exp); term->bname = ident; term->exp = exp; list_cons(decls, term); return decls; } static struct term *make_bind_rec(char *ident, struct term *exp, struct term *decls, struct info *locp) { /* Desugar let rec IDENT = EXP as * let IDENT = * let RLENS = (lns_make_rec) in * lns_check_rec ((lambda IDENT: EXP) RLENS) RLENS * where RLENS is a brandnew recursive lens. * * That only works since we know that 'let rec' is only defined for lenses, * not general purposes functions, i.e. we know that IDENT has type 'lens' * * The point of all this is that we make it possible to put a recursive * lens (which is a placeholder for the actual recursion) into arbitrary * places in some bigger lens and then have LNS_CHECK_REC rattle through * to do the special-purpose typechecking. */ char *id; struct info *info = exp->info; struct term *lambda = NULL, *rlens = NULL; struct term *app1 = NULL, *app2 = NULL, *app3 = NULL; id = strdup(ident); if (id == NULL) goto error; lambda = make_param(id, make_base_type(T_LENS), ref(info)); if (lambda == NULL) goto error; id = NULL; build_func(lambda, exp); rlens = make_term(A_VALUE, ref(exp->info)); if (rlens == NULL) goto error; rlens->value = lns_make_rec(ref(exp->info)); if (rlens->value == NULL) goto error; rlens->type = make_base_type(T_LENS); app1 = make_app_term(lambda, rlens, ref(info)); if (app1 == NULL) goto error; id = strdup(LNS_CHECK_REC_NAME); if (id == NULL) goto error; app2 = make_app_ident(id, app1, ref(info)); if (app2 == NULL) goto error; id = NULL; app3 = make_app_term(app2, ref(rlens), ref(info)); if (app3 == NULL) goto error; return make_bind(ident, NULL, app3, decls, locp); error: free(id); unref(lambda, term); unref(rlens, term); unref(app1, term); unref(app2, term); unref(app3, term); return NULL; } static struct term *make_let(char *ident, struct term *params, struct term *exp, struct term *body, struct info *locp) { /* let f (x:string) = "f " . x in f "a" . f "b" */ /* (lambda f: f "a" . f "b") (lambda x: "f " . x) */ /* (lambda IDENT: BODY) (lambda PARAMS: EXP) */ /* Desugar as (lambda IDENT: BODY) (lambda PARAMS: EXP) */ struct term *term = make_term_locp(A_LET, locp); struct term *p = make_param(ident, NULL, ref(term->info)); term->left = build_func(p, body); if (params != NULL) term->right = build_func(params, exp); else term->right = exp; return term; } static struct term *make_binop(enum term_tag tag, struct term *left, struct term *right, struct info *locp) { assert(tag == A_COMPOSE || tag == A_CONCAT || tag == A_UNION || tag == A_APP || tag == A_MINUS); struct term *term = make_term_locp(tag, locp); term->left = left; term->right = right; return term; } static struct term *make_unop(enum term_tag tag, struct term *exp, struct info *locp) { assert(tag == A_BRACKET); struct term *term = make_term_locp(tag, locp); term->brexp = exp; return term; } static struct term *make_ident(char *qname, struct info *locp) { struct term *term = make_term_locp(A_IDENT, locp); term->ident = make_string(qname); return term; } static struct term *make_unit_term(struct info *locp) { struct term *term = make_term_locp(A_VALUE, locp); term->value = make_unit(ref(term->info)); return term; } static struct term *make_string_term(char *value, struct info *locp) { struct term *term = make_term_locp(A_VALUE, locp); term->value = make_value(V_STRING, ref(term->info)); term->value->string = make_string(value); return term; } static struct term *make_regexp_term(char *pattern, int nocase, struct info *locp) { struct term *term = make_term_locp(A_VALUE, locp); term->value = make_value(V_REGEXP, ref(term->info)); term->value->regexp = make_regexp(term->info, pattern, nocase); return term; } static struct term *make_rep(struct term *exp, enum quant_tag quant, struct info *locp) { struct term *term = make_term_locp(A_REP, locp); term->quant = quant; term->exp = exp; return term; } static struct term *make_get_test(struct term *lens, struct term *arg, struct info *locp) { /* Return a term for "get" LENS ARG */ struct info *info = clone_info(locp); struct term *term = make_app_ident(strdup("get"), lens, info); term = make_app_term(term, arg, ref(info)); return term; } static struct term *make_put_test(struct term *lens, struct term *arg, struct term *cmds, struct info *locp) { /* Return a term for "put" LENS (CMDS ("get" LENS ARG)) ARG */ struct term *term = make_get_test(lens, arg, locp); term = make_app_term(cmds, term, ref(term->info)); struct term *put = make_app_ident(strdup("put"), ref(lens), ref(term->info)); put = make_app_term(put, term, ref(term->info)); put = make_app_term(put, ref(arg), ref(term->info)); return put; } static struct term *make_test(struct term *test, struct term *result, enum test_result_tag tr_tag, struct term *decls, struct info *locp) { struct term *term = make_term_locp(A_TEST, locp); term->tr_tag = tr_tag; term->test = test; term->result = result; term->next = decls; return term; } static struct term *make_tree_value(struct tree *tree, struct info *locp) { struct term *term = make_term_locp(A_VALUE, locp); struct value *value = make_value(V_TREE, ref(term->info)); value->origin = make_tree_origin(tree); term->value = value; return term; } static struct tree *tree_concat(struct tree *t1, struct tree *t2) { if (t2 != NULL) list_append(t1, t2); return t1; } void augl_error(struct info *locp, struct term **term, yyscan_t scanner, const char *s) { struct info info; struct string string; MEMZERO(&info, 1); info.ref = string.ref = UINT_MAX; info.filename = &string; if (locp != NULL) { info.first_line = locp->first_line; info.first_column = locp->first_column; info.last_line = locp->last_line; info.last_column = locp->last_column; info.filename->str = locp->filename->str; info.error = locp->error; } else if (scanner != NULL) { info.first_line = augl_get_lineno(scanner); info.first_column = augl_get_column(scanner); info.last_line = augl_get_lineno(scanner); info.last_column = augl_get_column(scanner); info.filename = augl_get_info(scanner)->filename; info.error = augl_get_info(scanner)->error; } else if (*term != NULL && (*term)->info != NULL) { memcpy(&info, (*term)->info, sizeof(info)); } else { info.first_line = info.last_line = 0; info.first_column = info.last_column = 0; } syntax_error(&info, "%s", s); } augeas-1.13.0/src/regexp.h0000644000175000017500000001061314161102026012206 00000000000000/* * regexp.h: wrappers for regexp handling * * Copyright (C) 2009-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #ifndef REGEXP_H_ #define REGEXP_H_ #include #include struct regexp { unsigned int ref; struct info *info; struct string *pattern; struct re_pattern_buffer *re; unsigned int nocase : 1; }; void print_regexp(FILE *out, struct regexp *regexp); /* Make a regexp with pattern PAT, which is not copied. Ownership * of INFO is taken. */ struct regexp *make_regexp(struct info *info, char *pat, int nocase); /* Make a regexp with pattern PAT, which is copied. Ownership of INFO is * taken. Escape sequences like \n in PAT are interpreted. */ struct regexp *make_regexp_unescape(struct info *info, const char *pat, int nocase); /* Return 1 if R is an empty pattern, i.e. one consisting of nothing but '(' and ')' characters, 0 otherwise */ int regexp_is_empty_pattern(struct regexp *r); /* Make a regexp that matches TEXT literally; the string TEXT * is not used by the returned rgexp and must be freed by the caller */ struct regexp *make_regexp_literal(struct info *info, const char *text); /* Make a regexp from a glob pattern */ struct regexp *make_regexp_from_glob(struct info *info, const char *glob); /* Do not call directly, use UNREF instead */ void free_regexp(struct regexp *regexp); /* Compile R->PATTERN into R->RE; return -1 and print an error * if compilation fails. Return 0 otherwise */ int regexp_compile(struct regexp *r); /* Check the syntax of R->PATTERN; return -1 if the pattern has a syntax * error, and a string indicating the error in *C. Return 0 if the pattern * is a valid regular expression. */ int regexp_check(struct regexp *r, const char **msg); /* Call RE_MATCH on R->RE and return its result; if R hasn't been compiled * yet, compile it. Return -3 if compilation fails */ int regexp_match(struct regexp *r, const char *string, const int size, const int start, struct re_registers *regs); /* Return 1 if R matches the empty string, 0 otherwise */ int regexp_matches_empty(struct regexp *r); /* Return the number of subexpressions (parentheses) inside R. May cause * compilation of R; return -1 if compilation fails. */ int regexp_nsub(struct regexp *r); struct regexp * regexp_union(struct info *, struct regexp *r1, struct regexp *r2); struct regexp * regexp_concat(struct info *, struct regexp *r1, struct regexp *r2); struct regexp * regexp_union_n(struct info *, int n, struct regexp **r); struct regexp * regexp_concat_n(struct info *, int n, struct regexp **r); struct regexp * regexp_iter(struct info *info, struct regexp *r, int min, int max); /* Return a new REGEXP that matches all the words matched by R1 but * not by R2 */ struct regexp * regexp_minus(struct info *info, struct regexp *r1, struct regexp *r2); struct regexp * regexp_maybe(struct info *info, struct regexp *r); struct regexp *regexp_make_empty(struct info *); /* Free up temporary data structures, most importantly compiled regular expressions */ void regexp_release(struct regexp *regexp); /* Produce a printable representation of R. The result will in general not be equivalent to the passed regular expression, but be easier for humans to read. */ char *regexp_escape(const struct regexp *r); /* If R is case-insensitive, expand its pattern so that it matches the same * string even when used in a case-sensitive match. */ char *regexp_expand_nocase(struct regexp *r); #endif /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/list.h0000644000175000017500000001626214161102026011675 00000000000000/* * list.h: Simple generic list manipulation * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #ifndef LIST_H_ #define LIST_H_ #define list_append(head, tail) \ do { \ if ((head) == NULL) { \ head = tail; \ break; \ } \ typeof(head) _p; \ for (_p = (head); _p->next != NULL; _p = _p->next); \ _p->next = (tail); \ } while (0) #define list_for_each(iter, list) \ for (typeof(list) (iter) = list; (iter) != NULL; (iter) = (iter)->next) #define list_remove(elt, list) \ do { \ typeof(elt) _e = (elt); \ if (_e == (list)) { \ (list) = _e->next; \ } else { \ typeof(_e) _p; \ for (_p = (list); _p != NULL && _p->next != _e; _p = _p->next); \ if (_p != NULL) \ _p->next = _e->next; \ } \ _e->next = NULL; \ } while(0) /* Insert NEW in list LIST before element AC. NEW->next must be null, and ELT must really be on LIST, otherwise chaos will ensue */ #define list_insert_before(new, elt, list) \ do { \ if ((list) == NULL) { \ (list) = (new); \ } else if ((elt) == (list)) { \ (new)->next = (elt); \ (list) = (new); \ } else { \ typeof(elt) _p; \ for (_p = (list); _p != NULL && _p->next != (elt); _p = _p->next); \ if (_p != NULL) { \ (new)->next = (elt); \ _p->next = (new); \ } \ } \ } while(0) #endif #define list_free(list) \ while ((list) != NULL) { \ typeof(list) _p = list; \ (list) = (list)->next; \ free((void *) _p); \ } #define list_length(len, list) \ do { \ typeof(list) _p; \ for (len=0, _p = (list); _p != NULL; len += 1, _p = _p->next); \ } while(0) /* Make ELT the new head of LIST and set LIST to it */ #define list_cons(list, elt) \ do { \ typeof(elt) _e = (elt); \ _e->next = (list); \ (list) = _e; \ } while(0) #define list_reverse(list) \ do { \ typeof(list) _head = (list); \ typeof(list) _prev = NULL; \ while (_head != NULL) { \ typeof(list) _next = _head->next; \ _head->next = _prev; \ _prev = _head; \ _head = _next; \ } \ (list) = _prev; \ } while (0) /* Append ELT to the end of LIST. TAIL must be NULL or a pointer to the last element of LIST. ELT may also be a list */ #define list_tail_cons(list, tail, elt) \ do { \ /* Append ELT at the end of LIST */ \ if ((list) == NULL) { \ (list) = (elt); \ } else { \ if ((tail) == NULL) \ for ((tail) = (list); (tail)->next != NULL; \ (tail) = (tail)->next); \ (tail)->next = (elt); \ } \ /* Make sure TAIL is the last element on the combined LIST */ \ (tail) = (elt); \ if ((tail) != NULL) \ while ((tail)->next != NULL) \ (tail) = (tail)->next; \ } while(0) /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/try0000755000175000017500000000244014161102026011306 00000000000000#!/usr/bin/env bash topdir=$(cd $(dirname $0)/.. && pwd) export AUGEAS_LENS_LIB=${topdir}/lenses export AUGEAS_ROOT=${topdir}/build/try AUGCMDS=${topdir}/build/augcmds.txt GDBCMDS=${topdir}/build/gdbcmds.txt rm -rf $AUGEAS_ROOT cp -pr ${topdir}/tests/root $AUGEAS_ROOT find $AUGEAS_ROOT -name \*.augnew\* | xargs -r rm if [[ ! -f $AUGCMDS ]] ; then cat > $AUGCMDS < $GDBCMDS < */ #include #include "augeas.h" #include "internal.h" #include "memory.h" #include "syntax.h" #include "transform.h" #include "errcode.h" #include #include #include #include #include /* Some popular labels that we use in /augeas */ static const char *const s_augeas = "augeas"; static const char *const s_files = "files"; static const char *const s_load = "load"; static const char *const s_pathx = "pathx"; static const char *const s_error = "error"; static const char *const s_pos = "pos"; static const char *const s_vars = "variables"; static const char *const s_lens = "lens"; static const char *const s_excl = "excl"; static const char *const s_incl = "incl"; #define AUGEAS_META_PATHX_FUNC AUGEAS_META_TREE "/version/pathx/functions" static const char *const static_nodes[][2] = { { AUGEAS_FILES_TREE, NULL }, { AUGEAS_META_TREE "/variables", NULL }, { AUGEAS_META_TREE "/version", PACKAGE_VERSION }, { AUGEAS_META_TREE "/version/save/mode[1]", AUG_SAVE_BACKUP_TEXT }, { AUGEAS_META_TREE "/version/save/mode[2]", AUG_SAVE_NEWFILE_TEXT }, { AUGEAS_META_TREE "/version/save/mode[3]", AUG_SAVE_NOOP_TEXT }, { AUGEAS_META_TREE "/version/save/mode[4]", AUG_SAVE_OVERWRITE_TEXT }, { AUGEAS_META_TREE "/version/defvar/expr", NULL }, { AUGEAS_META_PATHX_FUNC "/count", NULL }, { AUGEAS_META_PATHX_FUNC "/glob", NULL }, { AUGEAS_META_PATHX_FUNC "/label", NULL }, { AUGEAS_META_PATHX_FUNC "/last", NULL }, { AUGEAS_META_PATHX_FUNC "/modified", NULL }, { AUGEAS_META_PATHX_FUNC "/position", NULL }, { AUGEAS_META_PATHX_FUNC "/regexp", NULL } }; static const char *const errcodes[] = { "No error", /* AUG_NOERROR */ "Cannot allocate memory", /* AUG_ENOMEM */ "Internal error (please file a bug)", /* AUG_EINTERNAL */ "Invalid path expression", /* AUG_EPATHX */ "No match for path expression", /* AUG_ENOMATCH */ "Too many matches for path expression", /* AUG_EMMATCH */ "Syntax error in lens definition", /* AUG_ESYNTAX */ "Lens not found", /* AUG_ENOLENS */ "Multiple transforms", /* AUG_EMXFM */ "Node has no span info", /* AUG_ENOSPAN */ "Cannot move node into its descendant", /* AUG_EMVDESC */ "Failed to execute command", /* AUG_ECMDRUN */ "Invalid argument in function call", /* AUG_EBADARG */ "Invalid label", /* AUG_ELABEL */ "Cannot copy node into its descendant", /* AUG_ECPDESC */ "Cannot access file" /* AUG_EFILEACCESS */ }; static void tree_mark_dirty(struct tree *tree) { tree->dirty = 1; while (tree != tree->parent ) { if ( tree->file ) { tree->dirty = 1; break; } tree = tree->parent; } } void tree_clean(struct tree *tree) { if ( tree->file && ! tree->dirty ) return; list_for_each(c, tree->children) tree_clean(c); tree->dirty = 0; } struct tree *tree_child(struct tree *tree, const char *label) { if (tree == NULL) return NULL; list_for_each(child, tree->children) { if (streqv(label, child->label)) return child; } return NULL; } struct tree *tree_child_cr(struct tree *tree, const char *label) { static struct tree *child = NULL; if (tree == NULL) return NULL; child = tree_child(tree, label); if (child == NULL) { char *l = strdup(label); if (l == NULL) return NULL; child = tree_append(tree, l, NULL); } return child; } struct tree *tree_path_cr(struct tree *tree, int n, ...) { va_list ap; va_start(ap, n); for (int i=0; i < n; i++) { const char *l = va_arg(ap, const char *); tree = tree_child_cr(tree, l); } va_end(ap); return tree; } static struct tree *tree_fpath_int(struct augeas *aug, const char *fpath, bool create) { int r; char *steps = NULL, *step = NULL; size_t nsteps = 0; struct tree *result = NULL; r = argz_create_sep(fpath, '/', &steps, &nsteps); ERR_NOMEM(r < 0, aug); result = aug->origin; while ((step = argz_next(steps, nsteps, step))) { if (create) { result = tree_child_cr(result, step); ERR_THROW(result == NULL, aug, AUG_ENOMEM, "while searching %s: can not create %s", fpath, step); } else { /* Lookup only */ result = tree_child(result, step); if (result == NULL) goto done; } } done: free(steps); return result; error: result = NULL; goto done; } struct tree *tree_fpath(struct augeas *aug, const char *fpath) { return tree_fpath_int(aug, fpath, false); } struct tree *tree_fpath_cr(struct augeas *aug, const char *fpath) { return tree_fpath_int(aug, fpath, true); } struct tree *tree_find(struct augeas *aug, const char *path) { struct pathx *p = NULL; struct tree *result = NULL; int r; p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true); ERR_BAIL(aug); r = pathx_find_one(p, &result); BUG_ON(r > 1, aug, "Multiple matches for %s when only one was expected", path); done: free_pathx(p); return result; error: result = NULL; goto done; } struct tree *tree_find_cr(struct augeas *aug, const char *path) { struct pathx *p = NULL; struct tree *result = NULL; int r; p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true); ERR_BAIL(aug); r = pathx_expand_tree(p, &result); ERR_BAIL(aug); ERR_THROW(r < 0, aug, AUG_EINTERNAL, "pathx_expand_tree failed"); error: free_pathx(p); return result; } void tree_store_value(struct tree *tree, char **value) { if (streqv(tree->value, *value)) { free(*value); *value = NULL; return; } if (tree->value != NULL) { free(tree->value); tree->value = NULL; } if (*value != NULL) { tree->value = *value; *value = NULL; } tree_mark_dirty(tree); } int tree_set_value(struct tree *tree, const char *value) { char *v = NULL; if (streqv(tree->value, value)) return 0; if (value != NULL) { v = strdup(value); if (v == NULL) return -1; } tree_store_value(tree, &v); return 0; } static void store_error(const struct augeas *aug, const char *label, const char *value, int nentries, ...) { va_list ap; struct tree *tree; ensure(nentries % 2 == 0, aug); tree = tree_path_cr(aug->origin, 3, s_augeas, s_error, label); if (tree == NULL) return; tree_set_value(tree, value); va_start(ap, nentries); for (int i=0; i < nentries; i += 2) { char *l = va_arg(ap, char *); char *v = va_arg(ap, char *); struct tree *t = tree_child_cr(tree, l); if (t != NULL) tree_set_value(t, v); } va_end(ap); error: return; } /* Report pathx errors in /augeas/pathx/error */ static void store_pathx_error(const struct augeas *aug) { if (aug->error->code != AUG_EPATHX) return; store_error(aug, s_pathx, aug->error->minor_details, 2, s_pos, aug->error->details); } struct pathx *pathx_aug_parse(const struct augeas *aug, struct tree *tree, struct tree *root_ctx, const char *path, bool need_nodeset) { struct pathx *result; struct error *err = err_of_aug(aug); if (tree == NULL) tree = aug->origin; pathx_parse(tree, err, path, need_nodeset, aug->symtab, root_ctx, &result); return result; } /* Find the tree stored in AUGEAS_CONTEXT */ struct tree *tree_root_ctx(const struct augeas *aug) { struct pathx *p = NULL; struct tree *match = NULL; const char *ctx_path; int r; p = pathx_aug_parse(aug, aug->origin, NULL, AUGEAS_CONTEXT, true); ERR_BAIL(aug); r = pathx_find_one(p, &match); ERR_THROW(r > 1, aug, AUG_EMMATCH, "There are %d nodes matching %s, expecting one", r, AUGEAS_CONTEXT); if (match == NULL || match->value == NULL || *match->value == '\0') goto error; /* Clean via augrun's helper to ensure it's valid */ ctx_path = cleanpath(match->value); free_pathx(p); p = pathx_aug_parse(aug, aug->origin, NULL, ctx_path, true); ERR_BAIL(aug); if (pathx_first(p) == NULL) { r = pathx_expand_tree(p, &match); if (r < 0) goto done; r = tree_set_value(match, NULL); if (r < 0) goto done; } else { r = pathx_find_one(p, &match); ERR_THROW(r > 1, aug, AUG_EMMATCH, "There are %d nodes matching the context %s, expecting one", r, ctx_path); } done: free_pathx(p); return match; error: match = NULL; goto done; } struct tree *tree_append(struct tree *parent, char *label, char *value) { struct tree *result = make_tree(label, value, parent, NULL); if (result != NULL) list_append(parent->children, result); return result; } static struct tree *tree_append_s(struct tree *parent, const char *l0, char *v) { struct tree *result; char *l; if (l0 == NULL) { return NULL; } else { l = strdup(l0); } result = tree_append(parent, l, v); if (result == NULL) free(l); return result; } static struct tree *tree_from_transform(struct augeas *aug, const char *modname, struct transform *xfm) { struct tree *meta = tree_child_cr(aug->origin, s_augeas); struct tree *load = NULL, *txfm = NULL, *t; char *v = NULL; int r; ERR_NOMEM(meta == NULL, aug); load = tree_child_cr(meta, s_load); ERR_NOMEM(load == NULL, aug); if (modname == NULL) modname = "_"; txfm = tree_append_s(load, modname, NULL); ERR_NOMEM(txfm == NULL, aug); r = asprintf(&v, "@%s", modname); ERR_NOMEM(r < 0, aug); t = tree_append_s(txfm, s_lens, v); ERR_NOMEM(t == NULL, aug); v = NULL; list_for_each(f, xfm->filter) { const char *l = f->include ? s_incl : s_excl; v = strdup(f->glob->str); ERR_NOMEM(v == NULL, aug); t = tree_append_s(txfm, l, v); ERR_NOMEM(t == NULL, aug); } return txfm; error: free(v); tree_unlink(aug, txfm); return NULL; } /* Save user locale and switch to C locale */ #if HAVE_USELOCALE static void save_locale(struct augeas *aug) { if (aug->c_locale == NULL) { aug->c_locale = newlocale(LC_ALL_MASK, "C", NULL); ERR_NOMEM(aug->c_locale == NULL, aug); } aug->user_locale = uselocale(aug->c_locale); error: return; } #else static void save_locale(ATTRIBUTE_UNUSED struct augeas *aug) { } #endif #if HAVE_USELOCALE static void restore_locale(struct augeas *aug) { uselocale(aug->user_locale); aug->user_locale = NULL; } #else static void restore_locale(ATTRIBUTE_UNUSED struct augeas *aug) { } #endif /* Clean up old error messages every time we enter through the public * API. Since we make internal calls through the public API, we keep a * count of how many times a public API call was made, and only reset when * that count is 0. That requires that all public functions enclose their * work within a matching pair of api_entry/api_exit calls. */ void api_entry(const struct augeas *aug) { struct error *err = ((struct augeas *) aug)->error; ((struct augeas *) aug)->api_entries += 1; if (aug->api_entries > 1) return; reset_error(err); save_locale((struct augeas *) aug); } void api_exit(const struct augeas *aug) { assert(aug->api_entries > 0); ((struct augeas *) aug)->api_entries -= 1; if (aug->api_entries == 0) { store_pathx_error(aug); restore_locale((struct augeas *) aug); } } static int init_root(struct augeas *aug, const char *root0) { if (root0 == NULL) root0 = getenv(AUGEAS_ROOT_ENV); if (root0 == NULL || root0[0] == '\0') root0 = "/"; aug->root = strdup(root0); if (aug->root == NULL) return -1; if (aug->root[strlen(aug->root)-1] != SEP) { if (REALLOC_N(aug->root, strlen(aug->root) + 2) < 0) return -1; strcat((char *) aug->root, "/"); } return 0; } static int init_loadpath(struct augeas *aug, const char *loadpath) { int r; aug->modpathz = NULL; aug->nmodpath = 0; if (loadpath != NULL) { r = argz_add_sep(&aug->modpathz, &aug->nmodpath, loadpath, PATH_SEP_CHAR); if (r != 0) return -1; } char *env = getenv(AUGEAS_LENS_ENV); if (env != NULL) { r = argz_add_sep(&aug->modpathz, &aug->nmodpath, env, PATH_SEP_CHAR); if (r != 0) return -1; } if (!(aug->flags & AUG_NO_STDINC)) { r = argz_add(&aug->modpathz, &aug->nmodpath, AUGEAS_LENS_DIR); if (r != 0) return -1; r = argz_add(&aug->modpathz, &aug->nmodpath, AUGEAS_LENS_DIST_DIR); if (r != 0) return -1; } /* Clean up trailing slashes */ if (aug->nmodpath > 0) { argz_stringify(aug->modpathz, aug->nmodpath, PATH_SEP_CHAR); char *s, *t; const char *e = aug->modpathz + strlen(aug->modpathz); for (s = aug->modpathz, t = aug->modpathz; s < e; s++) { char *p = s; if (*p == '/') { while (*p == '/') p += 1; if (*p == '\0' || *p == PATH_SEP_CHAR) s = p; } if (t != s) *t++ = *s; else t += 1; } if (t != s) { *t = '\0'; } s = aug->modpathz; aug->modpathz = NULL; r = argz_create_sep(s, PATH_SEP_CHAR, &aug->modpathz, &aug->nmodpath); free(s); if (r != 0) return -1; } return 0; } static void init_save_mode(struct augeas *aug) { const char *v = AUG_SAVE_OVERWRITE_TEXT; if (aug->flags & AUG_SAVE_NEWFILE) { v = AUG_SAVE_NEWFILE_TEXT; } else if (aug->flags & AUG_SAVE_BACKUP) { v = AUG_SAVE_BACKUP_TEXT; } else if (aug->flags & AUG_SAVE_NOOP) { v = AUG_SAVE_NOOP_TEXT; } aug_set(aug, AUGEAS_META_SAVE_MODE, v); } struct augeas *aug_init(const char *root, const char *loadpath, unsigned int flags) { struct augeas *result; struct tree *tree_root = make_tree(NULL, NULL, NULL, NULL); int r; bool close_on_error = true; if (tree_root == NULL) return NULL; if (ALLOC(result) < 0) goto error; if (ALLOC(result->error) < 0) goto error; if (make_ref(result->error->info) < 0) goto error; result->error->info->error = result->error; result->error->info->filename = dup_string("(unknown file)"); if (result->error->info->filename == NULL) goto error; result->error->aug = result; result->origin = make_tree_origin(tree_root); if (result->origin == NULL) { free_tree(tree_root); goto error; } api_entry(result); result->flags = flags; r = init_root(result, root); ERR_NOMEM(r < 0, result); result->origin->children->label = strdup(s_augeas); /* We are now initialized enough that we can dare return RESULT even * when we encounter errors if the caller so wishes */ close_on_error = !(flags & AUG_NO_ERR_CLOSE); r = init_loadpath(result, loadpath); ERR_NOMEM(r < 0, result); /* We report the root dir in AUGEAS_META_ROOT, but we only use the value we store internally, to avoid any problems with AUGEAS_META_ROOT getting changed. */ aug_set(result, AUGEAS_META_ROOT, result->root); ERR_BAIL(result); /* Set the default path context */ aug_set(result, AUGEAS_CONTEXT, AUG_CONTEXT_DEFAULT); ERR_BAIL(result); for (int i=0; i < ARRAY_CARDINALITY(static_nodes); i++) { aug_set(result, static_nodes[i][0], static_nodes[i][1]); ERR_BAIL(result); } init_save_mode(result); ERR_BAIL(result); const char *v = (flags & AUG_ENABLE_SPAN) ? AUG_ENABLE : AUG_DISABLE; aug_set(result, AUGEAS_SPAN_OPTION, v); ERR_BAIL(result); if (interpreter_init(result) == -1) goto error; list_for_each(modl, result->modules) { struct transform *xform = modl->autoload; if (xform == NULL) continue; tree_from_transform(result, modl->name, xform); ERR_BAIL(result); } if (!(result->flags & AUG_NO_LOAD)) if (aug_load(result) < 0) goto error; api_exit(result); return result; error: if (close_on_error) { aug_close(result); result = NULL; } if (result != NULL && result->api_entries > 0) api_exit(result); return result; } /* Free one tree node */ static void free_tree_node(struct tree *tree) { if (tree == NULL) return; if (tree->span != NULL) free_span(tree->span); free(tree->label); free(tree->value); free(tree); } /* Only unlink; assume we know TREE is not in the symtab */ static int tree_unlink_raw(struct tree *tree) { int result = 0; assert (tree->parent != NULL); list_remove(tree, tree->parent->children); tree_mark_dirty(tree->parent); result = free_tree(tree->children) + 1; free_tree_node(tree); return result; } int tree_unlink(struct augeas *aug, struct tree *tree) { if (tree == NULL) return 0; pathx_symtab_remove_descendants(aug->symtab, tree); return tree_unlink_raw(tree); } void tree_unlink_children(struct augeas *aug, struct tree *tree) { if (tree == NULL) return; pathx_symtab_remove_descendants(aug->symtab, tree); while (tree->children != NULL) tree_unlink_raw(tree->children); } static void tree_mark_files(struct tree *tree) { if (tree_child(tree, "path") != NULL) { tree_mark_dirty(tree); } else { list_for_each(c, tree->children) { tree_mark_files(c); } } } static void tree_rm_dirty_files(struct augeas *aug, struct tree *tree) { struct tree *p; if (tree->file && !tree->dirty) { return; } else if (tree->file && tree->dirty && ((p = tree_child(tree, "path")) != NULL)) { tree_unlink(aug, tree_fpath(aug, p->value)); tree_unlink(aug, tree); } else { struct tree *c = tree->children; while (c != NULL) { struct tree *next = c->next; tree_rm_dirty_files(aug, c); c = next; } } } static void tree_rm_dirty_leaves(struct augeas *aug, struct tree *tree, struct tree *protect) { if (tree->file && !tree->dirty) return; struct tree *c = tree->children; while (c != NULL) { struct tree *next = c->next; tree_rm_dirty_leaves(aug, c, protect); c = next; } if (tree != protect && tree->children == NULL) tree_unlink(aug, tree); } int aug_load(struct augeas *aug) { const char *option = NULL; struct tree *meta = tree_child_cr(aug->origin, s_augeas); struct tree *meta_files = tree_child_cr(meta, s_files); struct tree *files = tree_child_cr(aug->origin, s_files); struct tree *load = tree_child_cr(meta, s_load); struct tree *vars = tree_child_cr(meta, s_vars); api_entry(aug); ERR_NOMEM(load == NULL, aug); /* To avoid unnecessary loads of files, we reload an existing file in * several steps: * (1) mark all file nodes under /augeas/files as dirty (and only those) * (2) process all files matched by a lens; we check (in * transform_load) if the file has been modified. If it has, we * reparse it. Either way, we clear the dirty flag. We also need to * reread the file if part or all of it has been modified in the * tree but not been saved yet * (3) remove all files from the tree that still have a dirty entry * under /augeas/files. Those files are not processed by any lens * anymore * (4) Remove entries from /augeas/files and /files that correspond * to directories without any files of interest */ /* update flags according to option value */ if (aug_get(aug, AUGEAS_SPAN_OPTION, &option) == 1) { if (strcmp(option, AUG_ENABLE) == 0) { aug->flags |= AUG_ENABLE_SPAN; } else { aug->flags &= ~AUG_ENABLE_SPAN; } } tree_clean(meta_files); tree_mark_files(meta_files); list_for_each(xfm, load->children) { if (transform_validate(aug, xfm) == 0) transform_load(aug, xfm, NULL); } /* This makes it possible to spot 'directories' that are now empty * because we removed their file contents */ tree_clean(files); tree_rm_dirty_files(aug, meta_files); tree_rm_dirty_leaves(aug, meta_files, meta_files); tree_rm_dirty_leaves(aug, files, files); tree_clean(aug->origin); list_for_each(v, vars->children) { aug_defvar(aug, v->label, v->value); ERR_BAIL(aug); } api_exit(aug); return 0; error: api_exit(aug); return -1; } static int find_one_node(struct pathx *p, struct tree **match) { struct error *err = err_of_pathx(p); int r = pathx_find_one(p, match); if (r == 1) return 0; if (r == 0) { report_error(err, AUG_ENOMATCH, NULL); } else { /* r > 1 */ report_error(err, AUG_EMMATCH, NULL); } return -1; } int aug_get(const struct augeas *aug, const char *path, const char **value) { struct pathx *p = NULL; struct tree *match; int r; if (value != NULL) *value = NULL; api_entry(aug); p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true); ERR_BAIL(aug); r = pathx_find_one(p, &match); ERR_BAIL(aug); ERR_THROW(r > 1, aug, AUG_EMMATCH, "There are %d nodes matching %s", r, path); if (r == 1 && value != NULL) *value = match->value; free_pathx(p); api_exit(aug); return r; error: free_pathx(p); api_exit(aug); return -1; } int aug_label(const struct augeas *aug, const char *path, const char **label) { struct pathx *p = NULL; struct tree *match; int r; api_entry(aug); p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true); ERR_BAIL(aug); if (label != NULL) *label = NULL; r = pathx_find_one(p, &match); ERR_BAIL(aug); ERR_THROW(r > 1, aug, AUG_EMMATCH, "There are %d nodes matching %s", r, path); if (r == 1 && label != NULL) *label = match->label; free_pathx(p); api_exit(aug); return r; error: free_pathx(p); api_exit(aug); return -1; } static void record_var_meta(struct augeas *aug, const char *name, const char *expr) { /* Record the definition of the variable */ struct tree *tree = tree_path_cr(aug->origin, 2, s_augeas, s_vars); ERR_NOMEM(tree == NULL, aug); if (expr == NULL) { tree_unlink(aug, tree_child(tree, name)); } else { tree = tree_child_cr(tree, name); ERR_NOMEM(tree == NULL, aug); tree_set_value(tree, expr); } error: return; } int aug_defvar(augeas *aug, const char *name, const char *expr) { struct pathx *p = NULL; int result = -1; api_entry(aug); if (expr == NULL) { result = pathx_symtab_undefine(&(aug->symtab), name); } else { p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), expr, false); ERR_BAIL(aug); result = pathx_symtab_define(&(aug->symtab), name, p); } ERR_BAIL(aug); record_var_meta(aug, name, expr); ERR_BAIL(aug); error: free_pathx(p); api_exit(aug); return result; } int aug_defnode(augeas *aug, const char *name, const char *expr, const char *value, int *created) { struct pathx *p = NULL; int result = -1; int r, cr; struct tree *tree; api_entry(aug); if (expr == NULL) goto error; if (created == NULL) created = &cr; p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), expr, false); ERR_BAIL(aug); if (pathx_first(p) == NULL) { r = pathx_expand_tree(p, &tree); if (r < 0) goto done; *created = 1; } else { *created = 0; } if (*created) { r = tree_set_value(tree, value); if (r < 0) goto done; result = pathx_symtab_assign_tree(&(aug->symtab), name, tree); char *e = path_of_tree(tree); ERR_NOMEM(e == NULL, aug) record_var_meta(aug, name, e); free(e); ERR_BAIL(aug); } else { result = pathx_symtab_define(&(aug->symtab), name, p); record_var_meta(aug, name, expr); ERR_BAIL(aug); } done: error: free_pathx(p); api_exit(aug); return result; } struct tree *tree_set(struct pathx *p, const char *value) { struct tree *tree; int r; r = pathx_expand_tree(p, &tree); if (r == -1) return NULL; r = tree_set_value(tree, value); if (r < 0) return NULL; return tree; } int aug_set(struct augeas *aug, const char *path, const char *value) { struct pathx *p = NULL; int result = -1; api_entry(aug); /* Get-out clause, in case context is broken */ struct tree *root_ctx = NULL; if (STRNEQ(path, AUGEAS_CONTEXT)) root_ctx = tree_root_ctx(aug); p = pathx_aug_parse(aug, aug->origin, root_ctx, path, true); ERR_BAIL(aug); result = tree_set(p, value) == NULL ? -1 : 0; error: free_pathx(p); api_exit(aug); return result; } int aug_setm(struct augeas *aug, const char *base, const char *sub, const char *value) { struct pathx *bx = NULL, *sx = NULL; struct tree *bt, *st; int result, r; api_entry(aug); bx = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), base, true); ERR_BAIL(aug); if (sub != NULL && STREQ(sub, ".")) sub = NULL; result = 0; for (bt = pathx_first(bx); bt != NULL; bt = pathx_next(bx)) { if (sub != NULL) { /* Handle subnodes of BT */ sx = pathx_aug_parse(aug, bt, NULL, sub, true); ERR_BAIL(aug); if (pathx_first(sx) != NULL) { /* Change existing subnodes matching SUB */ for (st = pathx_first(sx); st != NULL; st = pathx_next(sx)) { r = tree_set_value(st, value); ERR_NOMEM(r < 0, aug); result += 1; } } else { /* Create a new subnode matching SUB */ r = pathx_expand_tree(sx, &st); if (r == -1) goto error; r = tree_set_value(st, value); ERR_NOMEM(r < 0, aug); result += 1; } free_pathx(sx); sx = NULL; } else { /* Set nodes matching BT directly */ r = tree_set_value(bt, value); ERR_NOMEM(r < 0, aug); result += 1; } } done: free_pathx(bx); free_pathx(sx); api_exit(aug); return result; error: result = -1; goto done; } int tree_insert(struct pathx *p, const char *label, int before) { struct tree *new = NULL, *match; if (strchr(label, SEP) != NULL) return -1; if (find_one_node(p, &match) < 0) goto error; new = make_tree(strdup(label), NULL, match->parent, NULL); if (new == NULL || new->label == NULL) goto error; if (before) { list_insert_before(new, match, new->parent->children); } else { new->next = match->next; match->next = new; } return 0; error: free_tree(new); return -1; } int aug_insert(struct augeas *aug, const char *path, const char *label, int before) { struct pathx *p = NULL; int result = -1; api_entry(aug); p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true); ERR_BAIL(aug); result = tree_insert(p, label, before); error: free_pathx(p); api_exit(aug); return result; } struct tree *make_tree(char *label, char *value, struct tree *parent, struct tree *children) { struct tree *tree; if (ALLOC(tree) < 0) return NULL; tree->label = label; tree->value = value; tree->parent = parent; tree->children = children; list_for_each(c, tree->children) c->parent = tree; if (parent != NULL) tree_mark_dirty(tree); else tree->dirty = 1; return tree; } struct tree *make_tree_origin(struct tree *root) { struct tree *origin = NULL; origin = make_tree(NULL, NULL, NULL, root); if (origin == NULL) return NULL; origin->parent = origin; return origin; } /* Recursively free the whole tree TREE and all its siblings */ int free_tree(struct tree *tree) { int cnt = 0; while (tree != NULL) { struct tree *del = tree; tree = del->next; cnt += free_tree(del->children); free_tree_node(del); cnt += 1; } return cnt; } int tree_rm(struct pathx *p) { struct tree *tree, **del; int cnt = 0, ndel = 0, i; /* set ndel to the number of trees we could possibly delete */ for (tree = pathx_first(p); tree != NULL; tree = pathx_next(p)) { if (! TREE_HIDDEN(tree)) ndel += 1; } if (ndel == 0) return 0; if (ALLOC_N(del, ndel) < 0) { free(del); return -1; } for (i = 0, tree = pathx_first(p); tree != NULL; tree = pathx_next(p)) { if (TREE_HIDDEN(tree)) continue; pathx_symtab_remove_descendants(pathx_get_symtab(p), tree); /* Collect the tree nodes that actually need to be deleted in del. Mark the root of every subtree we are going to free by setting tree->added. Only add a node to del if none of its ancestors would have been freed by the time we get to freeing that node; this avoids double frees for situations where the path expression matches both /node and /node/child as unlinking /node implicitly unlinks /node/child */ int live = 1; for (struct tree *t = tree; live && ! ROOT_P(t); t = t->parent) { if (t->added) live = 0; } if (live) { del[i] = tree; i += 1; tree->added = 1; } } /* ndel now means: the number of trees we are actually going to delete */ ndel = i; for (i = 0; i < ndel; i++) { if (del[i] != NULL) { cnt += tree_unlink_raw(del[i]); } } free(del); return cnt; } int aug_rm(struct augeas *aug, const char *path) { struct pathx *p = NULL; int result = -1; api_entry(aug); p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true); ERR_BAIL(aug); result = tree_rm(p); error: free_pathx(p); api_exit(aug); return result; } int aug_span(struct augeas *aug, const char *path, char **filename, uint *label_start, uint *label_end, uint *value_start, uint *value_end, uint *span_start, uint *span_end) { struct pathx *p = NULL; int result = -1; struct tree *tree = NULL; struct span *span; api_entry(aug); p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true); ERR_BAIL(aug); tree = pathx_first(p); ERR_BAIL(aug); ERR_THROW(tree == NULL, aug, AUG_ENOMATCH, "No node matching %s", path); ERR_THROW(tree->span == NULL, aug, AUG_ENOSPAN, "No span info for %s", path); ERR_THROW(pathx_next(p) != NULL, aug, AUG_EMMATCH, "Multiple nodes match %s", path); span = tree->span; if (label_start != NULL) *label_start = span->label_start; if (label_end != NULL) *label_end = span->label_end; if (value_start != NULL) *value_start = span->value_start; if (value_end != NULL) *value_end = span->value_end; if (span_start != NULL) *span_start = span->span_start; if (span_end != NULL) *span_end = span->span_end; /* We are safer here, make sure we have a filename */ if (filename != NULL) { if (span->filename == NULL || span->filename->str == NULL) { *filename = strdup(""); } else { *filename = strdup(span->filename->str); } ERR_NOMEM(*filename == NULL, aug); } result = 0; error: free_pathx(p); api_exit(aug); return result; } int aug_mv(struct augeas *aug, const char *src, const char *dst) { struct pathx *s = NULL, *d = NULL; struct tree *ts, *td, *t; int r, ret; api_entry(aug); ret = -1; s = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), src, true); ERR_BAIL(aug); d = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), dst, true); ERR_BAIL(aug); r = find_one_node(s, &ts); if (r < 0) goto error; r = pathx_expand_tree(d, &td); if (r == -1) goto error; /* Don't move SRC into its own descendent */ t = td; do { ERR_THROW(t == ts, aug, AUG_EMVDESC, "destination %s is a descendant of %s", dst, src); t = t->parent; } while (t != aug->origin); free_tree(td->children); td->children = ts->children; list_for_each(c, td->children) { c->parent = td; } free(td->value); td->value = ts->value; ts->value = NULL; ts->children = NULL; tree_unlink(aug, ts); tree_mark_dirty(td); ret = 0; error: free_pathx(s); free_pathx(d); api_exit(aug); return ret; } static void tree_copy_rec(struct tree *src, struct tree *dst) { struct tree *n; char *value; list_for_each(c, src->children) { value = c->value == NULL ? NULL : strdup(c->value); n = tree_append_s(dst, c->label, value); tree_copy_rec(c, n); } } int aug_cp(struct augeas *aug, const char *src, const char *dst) { struct pathx *s = NULL, *d = NULL; struct tree *ts, *td, *t; int r, ret; api_entry(aug); ret = -1; s = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), src, true); ERR_BAIL(aug); d = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), dst, true); ERR_BAIL(aug); r = find_one_node(s, &ts); if (r < 0) goto error; r = pathx_expand_tree(d, &td); if (r == -1) goto error; /* Don't copy SRC into its own descendent */ t = td; do { ERR_THROW(t == ts, aug, AUG_ECPDESC, "destination %s is a descendant of %s", dst, src); t = t->parent; } while (t != aug->origin); tree_set_value(td, ts->value); free_tree(td->children); td->children = NULL; tree_copy_rec(ts, td); tree_mark_dirty(td); ret = 0; error: free_pathx(s); free_pathx(d); api_exit(aug); return ret; } int aug_rename(struct augeas *aug, const char *src, const char *lbl) { struct pathx *s = NULL; struct tree *ts; int ret; int count = 0; api_entry(aug); ret = -1; ERR_THROW(strchr(lbl, '/') != NULL, aug, AUG_ELABEL, "Label %s contains a /", lbl); s = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), src, true); ERR_BAIL(aug); for (ts = pathx_first(s); ts != NULL; ts = pathx_next(s)) { free(ts->label); ts->label = strdup(lbl); tree_mark_dirty(ts); count ++; } free_pathx(s); api_exit(aug); return count; error: free_pathx(s); api_exit(aug); return ret; } int aug_match(const struct augeas *aug, const char *pathin, char ***matches) { struct pathx *p = NULL; struct tree *tree; int cnt = 0; api_entry(aug); if (matches != NULL) *matches = NULL; if (STREQ(pathin, "/")) { pathin = "/*"; } p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), pathin, true); ERR_BAIL(aug); for (tree = pathx_first(p); tree != NULL; tree = pathx_next(p)) { if (! TREE_HIDDEN(tree)) cnt += 1; } ERR_BAIL(aug); if (matches == NULL) goto done; if (ALLOC_N(*matches, cnt) < 0) goto error; int i = 0; for (tree = pathx_first(p); tree != NULL; tree = pathx_next(p)) { if (TREE_HIDDEN(tree)) continue; (*matches)[i] = path_of_tree(tree); if ((*matches)[i] == NULL) { goto error; } i += 1; } ERR_BAIL(aug); done: free_pathx(p); api_exit(aug); return cnt; error: if (matches != NULL) { if (*matches != NULL) { for (i=0; i < cnt; i++) free((*matches)[i]); free(*matches); } } free_pathx(p); api_exit(aug); return -1; } /* XFM1 and XFM2 can both be used to save the same file. That is an error only if the two lenses in the two transforms are actually different. */ static int check_save_dup(struct augeas *aug, const char *path, struct tree *xfm1, struct tree *xfm2) { int result = 0; struct lens *l1 = xfm_lens(aug, xfm1, NULL); struct lens *l2 = xfm_lens(aug, xfm2, NULL); if (l1 != l2) { const char *filename = path + strlen(AUGEAS_FILES_TREE) + 1; transform_file_error(aug, "mxfm_save", filename, "Lenses %s and %s could be used to save this file", xfm_lens_name(xfm1), xfm_lens_name(xfm2)); ERR_REPORT(aug, AUG_EMXFM, "Path %s transformable by lens %s and %s", path, xfm_lens_name(xfm1), xfm_lens_name(xfm2)); result = -1; } return result; } static int tree_save(struct augeas *aug, struct tree *tree, const char *path) { int result = 0; struct tree *meta = tree_child_cr(aug->origin, s_augeas); struct tree *load = tree_child_cr(meta, s_load); // FIXME: We need to detect subtrees that aren't saved by anything if (load == NULL) return -1; list_for_each(t, tree) { if (t->file && ! t->dirty) { continue; } else { char *tpath = NULL; struct tree *transform = NULL; if (asprintf(&tpath, "%s/%s", path, t->label) == -1) { result = -1; continue; } if ( t->dirty ) { list_for_each(xfm, load->children) { if (transform_applies(xfm, tpath)) { if (transform == NULL || transform == xfm) { transform = xfm; } else { result = check_save_dup(aug, tpath, transform, xfm); } } } } if (transform != NULL) { int r = transform_save(aug, transform, tpath, t); if (r == -1) result = -1; } else { if (tree_save(aug, t->children, tpath) == -1) result = -1; } free(tpath); } } return result; } /* Reset the flags based on what is set in the tree. */ static int update_save_flags(struct augeas *aug) { const char *savemode ; aug_get(aug, AUGEAS_META_SAVE_MODE, &savemode); if (savemode == NULL) return -1; aug->flags &= ~(AUG_SAVE_BACKUP|AUG_SAVE_NEWFILE|AUG_SAVE_NOOP); if (STREQ(savemode, AUG_SAVE_NEWFILE_TEXT)) { aug->flags |= AUG_SAVE_NEWFILE; } else if (STREQ(savemode, AUG_SAVE_BACKUP_TEXT)) { aug->flags |= AUG_SAVE_BACKUP; } else if (STREQ(savemode, AUG_SAVE_NOOP_TEXT)) { aug->flags |= AUG_SAVE_NOOP ; } else if (STRNEQ(savemode, AUG_SAVE_OVERWRITE_TEXT)) { return -1; } return 0; } static int unlink_removed_files(struct augeas *aug, struct tree *files, struct tree *meta) { /* Find all nodes that correspond to a file and might have to be * unlinked. A node corresponds to a file if it has a child labelled * 'path', and we only consider it if there are no errors associated * with it */ static const char *const file_nodes = "descendant-or-self::*[path][count(error) = 0]"; int result = 0; if (files->file) return 0; for (struct tree *tm = meta->children; tm != NULL;) { struct tree *tf = tree_child(files, tm->label); struct tree *next = tm->next; if (tf == NULL) { /* Unlink all files in tm */ struct pathx *px = NULL; if (pathx_parse(tm, err_of_aug(aug), file_nodes, true, aug->symtab, NULL, &px) != PATHX_NOERROR) { result = -1; free_pathx(px); continue; } for (struct tree *t = pathx_first(px); t != NULL; t = pathx_next(px)) { if (remove_file(aug, t) < 0) result = -1; } free_pathx(px); } else if (! tree_child(tm, "path")) { if (unlink_removed_files(aug, tf, tm) < 0) result = -1; } tm = next; } return result; } int aug_save(struct augeas *aug) { int ret = 0; struct tree *meta = tree_child_cr(aug->origin, s_augeas); struct tree *meta_files = tree_child_cr(meta, s_files); struct tree *files = tree_child_cr(aug->origin, s_files); struct tree *load = tree_child_cr(meta, s_load); api_entry(aug); if (update_save_flags(aug) < 0) goto error; if (files == NULL || meta == NULL || load == NULL) goto error; aug_rm(aug, AUGEAS_EVENTS_SAVED); list_for_each(xfm, load->children) transform_validate(aug, xfm); if (tree_save(aug, files->children, AUGEAS_FILES_TREE) == -1) ret = -1; /* Remove files whose entire subtree was removed. */ if (meta_files != NULL) { if (unlink_removed_files(aug, files, meta_files) < 0) ret = -1; } if (!(aug->flags & AUG_SAVE_NOOP)) { tree_clean(aug->origin); } api_exit(aug); return ret; error: api_exit(aug); return -1; } static int print_one(FILE *out, const char *path, const char *value) { int r; r = fprintf(out, "%s", path); if (r < 0) return -1; if (value != NULL) { char *val = escape(value, -1, STR_ESCAPES); r = fprintf(out, " = \"%s\"", val); free(val); if (r < 0) return -1; } r = fputc('\n', out); if (r == EOF) return -1; return 0; } /* PATH is the path up to TREE's parent */ static int print_rec(FILE *out, struct tree *start, const char *ppath, int pr_hidden) { int r; char *path = NULL; list_for_each(tree, start) { if (TREE_HIDDEN(tree) && ! pr_hidden) continue; path = path_expand(tree, ppath); if (path == NULL) goto error; r = print_one(out, path, tree->value); if (r < 0) goto error; r = print_rec(out, tree->children, path, pr_hidden); free(path); path = NULL; if (r < 0) goto error; } return 0; error: free(path); return -1; } static int print_tree(FILE *out, struct pathx *p, int pr_hidden) { char *path = NULL; struct tree *tree; int r; for (tree = pathx_first(p); tree != NULL; tree = pathx_next(p)) { if (TREE_HIDDEN(tree) && ! pr_hidden) continue; path = path_of_tree(tree); if (path == NULL) goto error; r = print_one(out, path, tree->value); if (r < 0) goto error; r = print_rec(out, tree->children, path, pr_hidden); if (r < 0) goto error; free(path); path = NULL; } return 0; error: free(path); return -1; } int dump_tree(FILE *out, struct tree *tree) { struct pathx *p; int result; if (pathx_parse(tree, NULL, "/*", true, NULL, NULL, &p) != PATHX_NOERROR) { free_pathx(p); return -1; } result = print_tree(out, p, 1); free_pathx(p); return result; } int aug_text_store(augeas *aug, const char *lens, const char *node, const char *path) { struct pathx *p; const char *src; int result = -1, r; api_entry(aug); /* Validate PATH is syntactically correct */ p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true); free_pathx(p); ERR_BAIL(aug); r = aug_get(aug, node, &src); ERR_BAIL(aug); ERR_THROW(r == 0, aug, AUG_ENOMATCH, "Source node %s does not exist", node); ERR_THROW(src == NULL, aug, AUG_ENOMATCH, "Source node %s has a NULL value", node); result = text_store(aug, lens, path, src); error: api_exit(aug); return result; } int aug_text_retrieve(struct augeas *aug, const char *lens, const char *node_in, const char *path, const char *node_out) { struct tree *tree = NULL; const char *src; char *out = NULL; struct tree *tree_out; int r; api_entry(aug); tree = tree_find(aug, path); ERR_BAIL(aug); r = aug_get(aug, node_in, &src); ERR_BAIL(aug); ERR_THROW(r == 0, aug, AUG_ENOMATCH, "Source node %s does not exist", node_in); ERR_THROW(src == NULL, aug, AUG_ENOMATCH, "Source node %s has a NULL value", node_in); r = text_retrieve(aug, lens, path, tree, src, &out); if (r < 0) goto error; tree_out = tree_find_cr(aug, node_out); ERR_BAIL(aug); tree_store_value(tree_out, &out); api_exit(aug); return 0; error: free(out); api_exit(aug); return -1; } int aug_transform(struct augeas *aug, const char *lens, const char *file, int excl) { struct tree *meta = tree_child_cr(aug->origin, s_augeas); struct tree *load = tree_child_cr(meta, s_load); int r = 0, result = -1; struct tree *xfm = NULL, *lns = NULL, *t = NULL; const char *filter = NULL; char *p; int exists; char *lensname = NULL, *xfmname = NULL; api_entry(aug); ERR_NOMEM(meta == NULL || load == NULL, aug); ARG_CHECK(STREQ("", lens), aug, "aug_transform: LENS must not be empty"); ARG_CHECK(STREQ("", file), aug, "aug_transform: FILE must not be empty"); if ((p = strrchr(lens, '.'))) { lensname = strdup(lens); xfmname = strndup(lens, p - lens); ERR_NOMEM(lensname == NULL || xfmname == NULL, aug); } else { r = xasprintf(&lensname, "%s.lns", lens); xfmname = strdup(lens); ERR_NOMEM(r < 0 || xfmname == NULL, aug); } xfm = tree_child_cr(load, xfmname); ERR_NOMEM(xfm == NULL, aug); lns = tree_child_cr(xfm, s_lens); ERR_NOMEM(lns == NULL, aug); tree_store_value(lns, &lensname); exists = 0; filter = excl ? s_excl : s_incl; list_for_each(c, xfm->children) { if (c->value != NULL && STREQ(c->value, file) && streqv(c->label, filter)) { exists = 1; break; } } if (! exists) { t = tree_append_s(xfm, filter, NULL); ERR_NOMEM(t == NULL, aug); r = tree_set_value(t, file); ERR_NOMEM(r < 0, aug); } result = 0; error: free(lensname); free(xfmname); api_exit(aug); return result; } int aug_escape_name(augeas *aug, const char *in, char **out) { int result = -1; api_entry(aug); ARG_CHECK(in == NULL, aug, "aug_escape_name: IN must not be NULL"); ARG_CHECK(out == NULL, aug, "aug_escape_name: OUT must not be NULL"); result = pathx_escape_name(in, out); ERR_NOMEM(result < 0, aug); error: api_exit(aug); return result; } int aug_load_file(struct augeas *aug, const char *file) { int result = -1, r; struct tree *meta = tree_child_cr(aug->origin, s_augeas); struct tree *load = tree_child_cr(meta, s_load); char *tree_path = NULL; bool found = false; api_entry(aug); ERR_NOMEM(load == NULL, aug); list_for_each(xfm, load->children) { if (filter_matches(xfm, file)) { transform_load(aug, xfm, file); found = true; break; } } ERR_THROW(!found, aug, AUG_ENOLENS, "can not determine lens to load file %s", file); /* Mark the nodes we just loaded as clean so they won't get saved without additional modifications */ r = xasprintf(&tree_path, "/files/%s", file); ERR_NOMEM(r < 0, aug); struct tree *t = tree_fpath(aug, tree_path); if (t != NULL) { tree_clean(t); } result = 0; error: api_exit(aug); free(tree_path); return result; } int aug_print(const struct augeas *aug, FILE *out, const char *pathin) { struct pathx *p; int result = -1; api_entry(aug); if (pathin == NULL || strlen(pathin) == 0) { pathin = "/*"; } p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), pathin, true); ERR_BAIL(aug); result = print_tree(out, p, 0); error: free_pathx(p); api_exit(aug); return result; } static char * tree_source(const augeas *aug, struct tree *tree) { char *result = NULL; while (!(ROOT_P(tree) || tree->file)) tree = tree->parent; if (tree->file) { if (tree->span == NULL) { int r; r = ALLOC(tree->span); ERR_NOMEM(r < 0, aug); tree->span->filename = make_string(path_of_tree(tree)); ERR_NOMEM(tree->span->filename == NULL, aug); } result = strdup(tree->span->filename->str); ERR_NOMEM(result == NULL, aug); } error: return result; } int aug_source(const augeas *aug, const char *path, char **file_path) { int result = -1, r; struct pathx *p = NULL; struct tree *match; api_entry(aug); ARG_CHECK(file_path == NULL, aug, "aug_source_file: FILE_PATH must not be NULL"); *file_path = NULL; p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true); ERR_BAIL(aug); r = pathx_find_one(p, &match); ERR_BAIL(aug); ERR_THROW(r > 1, aug, AUG_EMMATCH, "There are %d nodes matching %s", r, path); ERR_THROW(r == 0, aug, AUG_ENOMATCH, "There is no node matching %s", path); *file_path = tree_source(aug, match); ERR_BAIL(aug); result = 0; error: free_pathx(p); api_exit(aug); return result; } int aug_preview(struct augeas *aug, const char *path, char **out) { struct tree *tree = NULL; struct pathx *p; int r; int result=-1; char *lens_path = NULL; char *lens_name = NULL; char *file_path = NULL; char *source_filename = NULL; char *source_text = NULL; *out = NULL; api_entry(aug); p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true); ERR_BAIL(aug); tree = pathx_first(p); ERR_BAIL(aug); ERR_THROW(tree == NULL, aug, AUG_ENOMATCH, "No node matching %s", path); file_path = tree_source(aug, tree); ERR_THROW(file_path == NULL, aug, AUG_EBADARG, "Path %s is not associated with a file", path); tree = tree_find(aug, file_path); xasprintf(&lens_path, "%s%s/%s", AUGEAS_META_TREE, file_path, s_lens); ERR_NOMEM(lens_path == NULL, aug); aug_get(aug,lens_path,(const char **) &lens_name); ERR_BAIL(aug); ERR_THROW(lens_name == NULL, aug, AUG_ENOLENS, "No lens found for path %s", path); xasprintf(&source_filename, "%s%s",aug->root, file_path + strlen(AUGEAS_FILES_TREE) + 1); ERR_NOMEM(source_filename == NULL, aug); source_text = xread_file(source_filename); ERR_THROW(source_text == NULL, aug, AUG_EFILEACCESS, "Cannot read file %s", source_filename); r = text_retrieve(aug, lens_name, file_path, tree, source_text, out); if (r < 0) goto error; result = 0; error: free(p); free(file_path); free(lens_path); free(source_filename); free(source_text); api_exit(aug); return result; } void aug_close(struct augeas *aug) { if (aug == NULL) return; /* There's no point in bothering with api_entry/api_exit here */ free_tree(aug->origin); unref(aug->modules, module); if (aug->error->exn != NULL) { aug->error->exn->ref = 0; free_value(aug->error->exn); aug->error->exn = NULL; } free((void *) aug->root); free(aug->modpathz); free_symtab(aug->symtab); unref(aug->error->info, info); free(aug->error->details); free(aug->error); free(aug); } int __aug_load_module_file(struct augeas *aug, const char *filename) { api_entry(aug); int r = load_module_file(aug, filename, NULL); api_exit(aug); return r; } int tree_equal(const struct tree *t1, const struct tree *t2) { while (t1 != NULL && t2 != NULL) { if (!streqv(t1->label, t2->label)) return 0; if (!streqv(t1->value, t2->value)) return 0; if (! tree_equal(t1->children, t2->children)) return 0; t1 = t1->next; t2 = t2->next; } return t1 == t2; } int aug_ns_attr(const augeas* aug, const char *var, int i, const char **value, const char **label, char **file_path) { int result = -1; if (value != NULL) *value = NULL; if (label != NULL) *label = NULL; if (file_path != NULL) *file_path = NULL; api_entry(aug); struct tree *tree = pathx_symtab_get_tree(aug->symtab, var, i); ERR_THROW(tree == NULL, aug, AUG_ENOMATCH, "Node %s[%d] does not exist", var, i); if (file_path != NULL) { *file_path = tree_source(aug, tree); ERR_BAIL(aug); } if (value != NULL) *value = tree->value; if (label != NULL) *label = tree->label; result = 1; error: api_exit(aug); return result; } int aug_ns_label(const augeas* aug, const char *var, int i, const char **label, int *index) { int result = -1; if (label != NULL) *label = NULL; if (index != NULL) *index = -1; api_entry(aug); struct tree *tree = pathx_symtab_get_tree(aug->symtab, var, i); ERR_THROW(tree == NULL, aug, AUG_ENOMATCH, "Node %s[%d] does not exist", var, i); if (label != NULL) *label = tree->label; if (index != NULL) { *index = tree_sibling_index(tree); } result = 1; error: api_exit(aug); return result; } int aug_ns_value(const augeas* aug, const char *var, int i, const char **value) { int result = -1; if (value != NULL) *value = NULL; api_entry(aug); struct tree *tree = pathx_symtab_get_tree(aug->symtab, var, i); ERR_THROW(tree == NULL, aug, AUG_ENOMATCH, "Node %s[%d] does not exist", var, i); if (value != NULL) *value = tree->value; result = 1; error: api_exit(aug); return result; } int aug_ns_count(const augeas *aug, const char *var) { int result = -1; api_entry(aug); result = pathx_symtab_count(aug->symtab, var); api_exit(aug); return result; } int aug_ns_path(const augeas *aug, const char *var, int i, char **path) { int result = -1; *path = NULL; api_entry(aug); struct tree *tree = pathx_symtab_get_tree(aug->symtab, var, i); ERR_THROW(tree == NULL, aug, AUG_ENOMATCH, "Node %s[%d] does not exist", var, i); *path = path_of_tree(tree); result = 0; error: api_exit(aug); return result; } /* * Error reporting API */ int aug_error(struct augeas *aug) { return aug->error->code; } const char *aug_error_message(struct augeas *aug) { aug_errcode_t errcode = aug->error->code; if (errcode >= ARRAY_CARDINALITY(errcodes)) errcode = AUG_EINTERNAL; return errcodes[errcode]; } const char *aug_error_minor_message(struct augeas *aug) { return aug->error->minor_details; } const char *aug_error_details(struct augeas *aug) { return aug->error->details; } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/lexer.c0000644000175000017500000020774414161102722012046 00000000000000#line 2 "lexer.c" /* config.h must precede flex's inclusion of in order for its _GNU_SOURCE definition to take effect. */ #include #line 7 "lexer.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 6 #define YY_FLEX_SUBMINOR_VERSION 4 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif #ifdef yy_create_buffer #define augl__create_buffer_ALREADY_DEFINED #else #define yy_create_buffer augl__create_buffer #endif #ifdef yy_delete_buffer #define augl__delete_buffer_ALREADY_DEFINED #else #define yy_delete_buffer augl__delete_buffer #endif #ifdef yy_scan_buffer #define augl__scan_buffer_ALREADY_DEFINED #else #define yy_scan_buffer augl__scan_buffer #endif #ifdef yy_scan_string #define augl__scan_string_ALREADY_DEFINED #else #define yy_scan_string augl__scan_string #endif #ifdef yy_scan_bytes #define augl__scan_bytes_ALREADY_DEFINED #else #define yy_scan_bytes augl__scan_bytes #endif #ifdef yy_init_buffer #define augl__init_buffer_ALREADY_DEFINED #else #define yy_init_buffer augl__init_buffer #endif #ifdef yy_flush_buffer #define augl__flush_buffer_ALREADY_DEFINED #else #define yy_flush_buffer augl__flush_buffer #endif #ifdef yy_load_buffer_state #define augl__load_buffer_state_ALREADY_DEFINED #else #define yy_load_buffer_state augl__load_buffer_state #endif #ifdef yy_switch_to_buffer #define augl__switch_to_buffer_ALREADY_DEFINED #else #define yy_switch_to_buffer augl__switch_to_buffer #endif #ifdef yypush_buffer_state #define augl_push_buffer_state_ALREADY_DEFINED #else #define yypush_buffer_state augl_push_buffer_state #endif #ifdef yypop_buffer_state #define augl_pop_buffer_state_ALREADY_DEFINED #else #define yypop_buffer_state augl_pop_buffer_state #endif #ifdef yyensure_buffer_stack #define augl_ensure_buffer_stack_ALREADY_DEFINED #else #define yyensure_buffer_stack augl_ensure_buffer_stack #endif #ifdef yylex #define augl_lex_ALREADY_DEFINED #else #define yylex augl_lex #endif #ifdef yyrestart #define augl_restart_ALREADY_DEFINED #else #define yyrestart augl_restart #endif #ifdef yylex_init #define augl_lex_init_ALREADY_DEFINED #else #define yylex_init augl_lex_init #endif #ifdef yylex_init_extra #define augl_lex_init_extra_ALREADY_DEFINED #else #define yylex_init_extra augl_lex_init_extra #endif #ifdef yylex_destroy #define augl_lex_destroy_ALREADY_DEFINED #else #define yylex_destroy augl_lex_destroy #endif #ifdef yyget_debug #define augl_get_debug_ALREADY_DEFINED #else #define yyget_debug augl_get_debug #endif #ifdef yyset_debug #define augl_set_debug_ALREADY_DEFINED #else #define yyset_debug augl_set_debug #endif #ifdef yyget_extra #define augl_get_extra_ALREADY_DEFINED #else #define yyget_extra augl_get_extra #endif #ifdef yyset_extra #define augl_set_extra_ALREADY_DEFINED #else #define yyset_extra augl_set_extra #endif #ifdef yyget_in #define augl_get_in_ALREADY_DEFINED #else #define yyget_in augl_get_in #endif #ifdef yyset_in #define augl_set_in_ALREADY_DEFINED #else #define yyset_in augl_set_in #endif #ifdef yyget_out #define augl_get_out_ALREADY_DEFINED #else #define yyget_out augl_get_out #endif #ifdef yyset_out #define augl_set_out_ALREADY_DEFINED #else #define yyset_out augl_set_out #endif #ifdef yyget_leng #define augl_get_leng_ALREADY_DEFINED #else #define yyget_leng augl_get_leng #endif #ifdef yyget_text #define augl_get_text_ALREADY_DEFINED #else #define yyget_text augl_get_text #endif #ifdef yyget_lineno #define augl_get_lineno_ALREADY_DEFINED #else #define yyget_lineno augl_get_lineno #endif #ifdef yyset_lineno #define augl_set_lineno_ALREADY_DEFINED #else #define yyset_lineno augl_set_lineno #endif #ifdef yyget_column #define augl_get_column_ALREADY_DEFINED #else #define yyget_column augl_get_column #endif #ifdef yyset_column #define augl_set_column_ALREADY_DEFINED #else #define yyset_column augl_set_column #endif #ifdef yywrap #define augl_wrap_ALREADY_DEFINED #else #define yywrap augl_wrap #endif #ifdef yyget_lval #define augl_get_lval_ALREADY_DEFINED #else #define yyget_lval augl_get_lval #endif #ifdef yyset_lval #define augl_set_lval_ALREADY_DEFINED #else #define yyset_lval augl_set_lval #endif #ifdef yyget_lloc #define augl_get_lloc_ALREADY_DEFINED #else #define yyget_lloc augl_get_lloc #endif #ifdef yyset_lloc #define augl_set_lloc_ALREADY_DEFINED #else #define yyset_lloc augl_set_lloc #endif #ifdef yyalloc #define augl_alloc_ALREADY_DEFINED #else #define yyalloc augl_alloc #endif #ifdef yyrealloc #define augl_realloc_ALREADY_DEFINED #else #define yyrealloc augl_realloc #endif #ifdef yyfree #define augl_free_ALREADY_DEFINED #else #define yyfree augl_free #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #ifndef SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ /* begin standard C++ headers. */ /* TODO: this is always defined, so inline it */ #define yyconst const #if defined(__GNUC__) && __GNUC__ >= 3 #define yynoreturn __attribute__((__noreturn__)) #else #define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an * integer in range [0..255] for use as an array index. */ #define YY_SC_TO_UI(c) ((YY_CHAR) (c)) /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yyrestart( yyin , yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires * access to the local variable yy_act. Since yyless() is a macro, it would break * existing scanners that call yyless() from OUTSIDE yylex. * One obvious solution it to make yy_act a global. I tried that, and saw * a 5% performance hit in a non-yylineno scanner, because yy_act is * normally declared as a register variable-- so it is not worth it. */ #define YY_LESS_LINENO(n) \ do { \ int yyl;\ for ( yyl = n; yyl < yyleng; ++yyl )\ if ( yytext[yyl] == '\n' )\ --yylineno;\ }while(0) #define YY_LINENO_REWIND_TO(dst) \ do {\ const char *p;\ for ( p = yy_cp-1; p >= (dst); --p)\ if ( *p == '\n' )\ --yylineno;\ }while(0) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] void yyrestart ( FILE *input_file , yyscan_t yyscanner ); void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); void yypop_buffer_state ( yyscan_t yyscanner ); static void yyensure_buffer_stack ( yyscan_t yyscanner ); static void yy_load_buffer_state ( yyscan_t yyscanner ); static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); #define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); void *yyalloc ( yy_size_t , yyscan_t yyscanner ); void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); void yyfree ( void * , yyscan_t yyscanner ); #define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define augl_wrap(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP typedef flex_uint8_t YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); static int yy_get_next_buffer ( yyscan_t yyscanner ); static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ yyleng = (int) (yy_cp - yy_bp); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 29 #define YY_END_OF_BUFFER 30 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static const flex_int16_t yy_accept[95] = { 0, 1, 1, 1, 1, 30, 25, 1, 2, 25, 25, 7, 7, 7, 25, 23, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 28, 1, 28, 28, 28, 1, 2, 3, 0, 4, 0, 24, 20, 0, 6, 0, 0, 23, 22, 22, 22, 22, 14, 22, 22, 22, 22, 22, 22, 26, 27, 0, 5, 21, 22, 22, 17, 22, 10, 22, 18, 22, 22, 22, 21, 22, 22, 13, 0, 22, 22, 22, 16, 19, 22, 0, 22, 22, 22, 22, 0, 8, 12, 11, 22, 0, 15, 9, 0 } ; static const YY_CHAR yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 5, 1, 1, 1, 1, 1, 6, 7, 8, 9, 1, 10, 11, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 9, 9, 1, 9, 14, 9, 1, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 9, 16, 9, 1, 17, 1, 18, 17, 19, 20, 21, 22, 23, 17, 24, 17, 17, 25, 26, 27, 28, 29, 17, 30, 31, 32, 33, 17, 17, 34, 17, 17, 9, 9, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static const YY_CHAR yy_meta[35] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 3, 1, 3, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 } ; static const flex_int16_t yy_base[102] = { 0, 0, 0, 33, 40, 134, 135, 131, 129, 128, 33, 122, 135, 115, 35, 117, 0, 23, 106, 99, 104, 96, 90, 101, 89, 99, 135, 117, 115, 109, 109, 113, 111, 109, 45, 135, 0, 135, 135, 41, 88, 0, 0, 100, 0, 78, 77, 76, 0, 27, 87, 74, 82, 74, 72, 135, 135, 99, 135, 0, 80, 72, 0, 68, 96, 64, 0, 75, 71, 62, 0, 63, 67, 0, 38, 66, 56, 62, 0, 0, 60, 46, 45, 36, 41, 42, 39, 0, 0, 0, 35, 60, 0, 135, 135, 68, 72, 76, 79, 81, 48, 83 } ; static const flex_int16_t yy_def[102] = { 0, 94, 1, 95, 95, 94, 94, 94, 94, 94, 96, 94, 94, 94, 97, 98, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 94, 94, 94, 94, 94, 94, 94, 94, 96, 94, 96, 94, 94, 97, 94, 97, 100, 98, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 94, 94, 94, 94, 101, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 101, 99, 99, 99, 94, 99, 99, 99, 99, 99, 99, 94, 99, 99, 99, 99, 94, 99, 99, 99, 99, 94, 99, 94, 0, 94, 94, 94, 94, 94, 94, 94 } ; static const flex_int16_t yy_nxt[170] = { 0, 6, 7, 8, 9, 10, 11, 12, 12, 12, 13, 12, 14, 6, 6, 15, 6, 16, 17, 16, 16, 16, 16, 18, 19, 20, 21, 16, 16, 22, 23, 24, 25, 16, 16, 27, 8, 28, 35, 29, 74, 30, 27, 8, 28, 45, 29, 40, 30, 36, 35, 41, 59, 40, 63, 92, 46, 41, 91, 64, 90, 36, 93, 93, 89, 88, 87, 86, 81, 26, 26, 26, 26, 34, 34, 34, 34, 39, 39, 39, 39, 43, 43, 43, 44, 44, 70, 70, 85, 84, 83, 82, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 33, 69, 68, 67, 66, 65, 62, 61, 60, 42, 58, 57, 32, 31, 56, 55, 33, 31, 54, 53, 52, 51, 50, 49, 48, 47, 42, 38, 37, 33, 32, 31, 94, 5, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94 } ; static const flex_int16_t yy_chk[170] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 10, 3, 74, 3, 4, 4, 4, 17, 4, 14, 4, 10, 34, 14, 100, 39, 49, 90, 17, 39, 86, 49, 85, 34, 91, 91, 84, 83, 82, 81, 74, 95, 95, 95, 95, 96, 96, 96, 96, 97, 97, 97, 97, 98, 98, 98, 99, 99, 101, 101, 80, 77, 76, 75, 72, 71, 69, 68, 67, 65, 64, 63, 61, 60, 57, 54, 53, 52, 51, 50, 47, 46, 45, 43, 40, 33, 32, 31, 30, 29, 28, 27, 25, 24, 23, 22, 21, 20, 19, 18, 15, 13, 11, 9, 8, 7, 5, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94 } ; /* Table of booleans, true if rule could match eol. */ static const flex_int32_t yy_rule_can_match_eol[30] = { 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET #line 1 "lexer.l" /* Scanner for config specs -*- C -*- */ #define YY_NO_INPUT 1 #line 16 "lexer.l" #include "syntax.h" #include "errcode.h" typedef struct info YYLTYPE; #define YYLTYPE_IS_DECLARED 1 #include "parser.h" /* Advance of NUM lines. */ # define LOCATION_LINES(Loc, Num) \ (Loc).last_column = 0; \ (Loc).last_line += Num; /* Restart: move the first cursor to the last position. */ # define LOCATION_STEP(Loc) \ (Loc).first_column = (Loc).last_column; \ (Loc).first_line = (Loc).last_line; /* The lack of reference counting for filename is intentional */ #define YY_USER_ACTION \ do { \ yylloc->last_column += yyleng; \ yylloc->filename = augl_get_info(yyscanner)->filename; \ yylloc->error = augl_get_info(yyscanner)->error; \ } while(0); #define YY_USER_INIT LOCATION_STEP(*yylloc) #define YY_EXTRA_TYPE struct state * int augl_init_lexer(struct state *state, yyscan_t * scanner); void augl_close_lexer(yyscan_t *scanner); struct info *augl_get_info(yyscan_t yyscanner); static void loc_update(YYLTYPE *yylloc, const char *s, int len) { for (int i=0; i < len; i++) { if (s[i] == '\n') { LOCATION_LINES(*yylloc, 1); } } } static char *regexp_literal(const char *s, int len) { char *u = unescape(s, len, RX_ESCAPES); if (u == NULL) return NULL; size_t u_len = strlen(u); regexp_c_locale(&u, &u_len); return u; } #line 810 "lexer.c" #line 812 "lexer.c" #define INITIAL 0 #define COMMENT 1 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif /* Holds the entire state of the reentrant scanner. */ struct yyguts_t { /* User-defined. Not touched by flex. */ YY_EXTRA_TYPE yyextra_r; /* The rest are the same as the globals declared in the non-reentrant scanner. */ FILE *yyin_r, *yyout_r; size_t yy_buffer_stack_top; /**< index of top of stack. */ size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; int yy_n_chars; int yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; int yy_did_buffer_switch_on_eof; int yy_start_stack_ptr; int yy_start_stack_depth; int *yy_start_stack; yy_state_type yy_last_accepting_state; char* yy_last_accepting_cpos; int yylineno_r; int yy_flex_debug_r; char *yytext_r; int yy_more_flag; int yy_more_len; YYSTYPE * yylval_r; YYLTYPE * yylloc_r; }; /* end struct yyguts_t */ static int yy_init_globals ( yyscan_t yyscanner ); /* This must go here because YYSTYPE and YYLTYPE are included * from bison output in section 1.*/ # define yylval yyg->yylval_r # define yylloc yyg->yylloc_r int yylex_init (yyscan_t* scanner); int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int yylex_destroy ( yyscan_t yyscanner ); int yyget_debug ( yyscan_t yyscanner ); void yyset_debug ( int debug_flag , yyscan_t yyscanner ); YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); FILE *yyget_in ( yyscan_t yyscanner ); void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); FILE *yyget_out ( yyscan_t yyscanner ); void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); int yyget_leng ( yyscan_t yyscanner ); char *yyget_text ( yyscan_t yyscanner ); int yyget_lineno ( yyscan_t yyscanner ); void yyset_lineno ( int _line_number , yyscan_t yyscanner ); int yyget_column ( yyscan_t yyscanner ); void yyset_column ( int _column_no , yyscan_t yyscanner ); YYSTYPE * yyget_lval ( yyscan_t yyscanner ); void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yywrap ( yyscan_t yyscanner ); #else extern int yywrap ( yyscan_t yyscanner ); #endif #endif #ifndef YY_NO_UNPUT #endif #ifndef yytext_ptr static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen ( const char * , yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput ( yyscan_t yyscanner ); #else static int input ( yyscan_t yyscanner ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ int n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int yylex \ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); #define YY_DECL int yylex \ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK /*LINTED*/break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { yy_state_type yy_current_state; char *yy_cp, *yy_bp; int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; yylloc = yylloc_param; if ( !yyg->yy_init ) { yyg->yy_init = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! yyg->yy_start ) yyg->yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } yy_load_buffer_state( yyscanner ); } { #line 81 "lexer.l" #line 1097 "lexer.c" while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { yy_cp = yyg->yy_c_buf_p; /* Support of yytext. */ *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yyg->yy_start; yy_match: do { YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 95 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++yy_cp; } while ( yy_current_state != 94 ); yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; yy_find_action: yy_act = yy_accept[yy_current_state]; YY_DO_BEFORE_ACTION; if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) { int yyl; for ( yyl = 0; yyl < yyleng; ++yyl ) if ( yytext[yyl] == '\n' ) do{ yylineno++; yycolumn=0; }while(0) ; } do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yyg->yy_hold_char; yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; case 1: YY_RULE_SETUP #line 84 "lexer.l" LOCATION_STEP(*yylloc); YY_BREAK case 2: /* rule 2 can match eol */ YY_RULE_SETUP #line 85 "lexer.l" LOCATION_LINES(*yylloc, yyleng); LOCATION_STEP(*yylloc); YY_BREAK case 3: /* rule 3 can match eol */ YY_RULE_SETUP #line 86 "lexer.l" LOCATION_LINES(*yylloc, yyleng/2); LOCATION_STEP(*yylloc); YY_BREAK case 4: /* rule 4 can match eol */ YY_RULE_SETUP #line 91 "lexer.l" { loc_update(yylloc, yytext, yyleng); yylval->string = unescape(yytext+1, yyleng-2, STR_ESCAPES); return DQUOTED; } YY_BREAK case 5: /* rule 5 can match eol */ YY_RULE_SETUP #line 97 "lexer.l" { loc_update(yylloc, yytext, yyleng); yylval->regexp.nocase = 1; yylval->regexp.pattern = regexp_literal(yytext+1, yyleng-3); return REGEXP; } YY_BREAK case 6: /* rule 6 can match eol */ YY_RULE_SETUP #line 104 "lexer.l" { loc_update(yylloc, yytext, yyleng); yylval->regexp.nocase = 0; yylval->regexp.pattern = regexp_literal(yytext+1, yyleng-2); return REGEXP; } YY_BREAK case 7: YY_RULE_SETUP #line 111 "lexer.l" return yytext[0]; YY_BREAK case 8: YY_RULE_SETUP #line 113 "lexer.l" return KW_MODULE; YY_BREAK case 9: /* rule 9 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ YY_LINENO_REWIND_TO(yy_cp - 1); yyg->yy_c_buf_p = yy_cp -= 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 115 "lexer.l" return KW_LET_REC; YY_BREAK case 10: YY_RULE_SETUP #line 117 "lexer.l" return KW_LET; YY_BREAK case 11: YY_RULE_SETUP #line 118 "lexer.l" return KW_STRING; YY_BREAK case 12: YY_RULE_SETUP #line 119 "lexer.l" return KW_REGEXP; YY_BREAK case 13: YY_RULE_SETUP #line 120 "lexer.l" return KW_LENS; YY_BREAK case 14: YY_RULE_SETUP #line 121 "lexer.l" return KW_IN; YY_BREAK case 15: YY_RULE_SETUP #line 122 "lexer.l" return KW_AUTOLOAD; YY_BREAK /* tests */ case 16: YY_RULE_SETUP #line 125 "lexer.l" return KW_TEST; YY_BREAK case 17: YY_RULE_SETUP #line 126 "lexer.l" return KW_GET; YY_BREAK case 18: YY_RULE_SETUP #line 127 "lexer.l" return KW_PUT; YY_BREAK case 19: YY_RULE_SETUP #line 128 "lexer.l" return KW_AFTER; YY_BREAK case 20: YY_RULE_SETUP #line 130 "lexer.l" return ARROW; YY_BREAK case 21: YY_RULE_SETUP #line 132 "lexer.l" { yylval->string = strndup(yytext, yyleng); return QIDENT; } YY_BREAK case 22: YY_RULE_SETUP #line 136 "lexer.l" { yylval->string = strndup(yytext, yyleng); return LIDENT; } YY_BREAK case 23: YY_RULE_SETUP #line 140 "lexer.l" { yylval->string = strndup(yytext, yyleng); return UIDENT; } YY_BREAK case 24: YY_RULE_SETUP #line 144 "lexer.l" { augl_get_extra(yyscanner)->comment_depth = 1; BEGIN(COMMENT); } YY_BREAK case 25: YY_RULE_SETUP #line 148 "lexer.l" { report_error(augl_get_info(yyscanner)->error, AUG_ESYNTAX, "%s:%d:%d: Unexpected character %c", augl_get_info(yyscanner)->filename->str, yylineno, yylloc->first_column, yytext[0]); } YY_BREAK case YY_STATE_EOF(INITIAL): #line 155 "lexer.l" { augl_close_lexer(yyscanner); yyterminate(); } YY_BREAK case 26: YY_RULE_SETUP #line 164 "lexer.l" { augl_get_extra(yyscanner)->comment_depth += 1; } YY_BREAK case 27: YY_RULE_SETUP #line 167 "lexer.l" { augl_get_extra(yyscanner)->comment_depth -= 1; if (augl_get_extra(yyscanner)->comment_depth == 0) BEGIN(INITIAL); } YY_BREAK case 28: YY_RULE_SETUP #line 172 "lexer.l" /* Skip */; YY_BREAK case YY_STATE_EOF(COMMENT): #line 173 "lexer.l" { report_error(augl_get_info(yyscanner)->error, AUG_ESYNTAX, "%s:%d:%d: Missing *)", augl_get_info(yyscanner)->filename->str, yylineno, yylloc->first_column); augl_close_lexer(yyscanner); yyterminate(); } YY_BREAK case 29: YY_RULE_SETUP #line 182 "lexer.l" YY_FATAL_ERROR( "flex scanner jammed" ); YY_BREAK #line 1380 "lexer.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( yywrap( yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; char *source = yyg->yytext_ptr; int number_to_move, i; int ret_val; if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ yyrealloc( (void *) b->yy_ch_buf, (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart( yyin , yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); /* "- 2" to take care of EOB's */ YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); } yyg->yy_n_chars += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { yy_state_type yy_current_state; char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_current_state = yyg->yy_start; for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 95 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ char *yy_cp = yyg->yy_c_buf_p; YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 95 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; yy_is_jam = (yy_current_state == 94); (void)yyg; return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_UNPUT #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *yyg->yy_c_buf_p = yyg->yy_hold_char; if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart( yyin , yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap( yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ yyg->yy_hold_char = *++yyg->yy_c_buf_p; if ( c == '\n' ) do{ yylineno++; yycolumn=0; }while(0) ; return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ void yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); yy_load_buffer_state( yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * @param yyscanner The scanner object. */ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* TODO. We should be able to replace this entire function body * with * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; yy_load_buffer_state( yyscanner ); /* We don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void yy_load_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * @param yyscanner The scanner object. * @return the allocated buffer state. */ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer( b, file , yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * @param yyscanner The scanner object. */ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree( (void *) b->yy_ch_buf , yyscanner ); yyfree( (void *) b , yyscanner ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flush_buffer( b , yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then yy_init_buffer was _probably_ * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * @param yyscanner The scanner object. */ void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) yy_load_buffer_state( yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * @param yyscanner The scanner object. */ void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; yyensure_buffer_stack(yyscanner); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from yy_switch_to_buffer. */ yy_load_buffer_state( yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * @param yyscanner The scanner object. */ void yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { yy_load_buffer_state( yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void yyensure_buffer_stack (yyscan_t yyscanner) { yy_size_t num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; yyg->yy_buffer_stack_top = 0; return; } if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ yy_size_t grow_size = 8 /* arbitrary grow size */; num_to_alloc = yyg->yy_buffer_stack_max + grow_size; yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return NULL; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = NULL; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; yy_switch_to_buffer( b , yyscanner ); return b; } /** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * yy_scan_bytes() instead. */ YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) { return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); } /** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = (yy_size_t) (_yybytes_len + 2); buf = (char *) yyalloc( n , yyscanner ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = yy_scan_buffer( buf, n , yyscanner); if ( ! b ) YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = yyg->yy_hold_char; \ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ yyg->yy_hold_char = *yyg->yy_c_buf_p; \ *yyg->yy_c_buf_p = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the user-defined data for this scanner. * @param yyscanner The scanner object. */ YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyextra; } /** Get the current line number. * @param yyscanner The scanner object. */ int yyget_lineno (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yylineno; } /** Get the current column number. * @param yyscanner The scanner object. */ int yyget_column (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yycolumn; } /** Get the input stream. * @param yyscanner The scanner object. */ FILE *yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ int yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ char *yyget_text (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yytext; } /** Set the user-defined data. This data is never touched by the scanner. * @param user_defined The data to be associated with this scanner. * @param yyscanner The scanner object. */ void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current line number. * @param _line_number line number * @param yyscanner The scanner object. */ void yyset_lineno (int _line_number , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); yylineno = _line_number; } /** Set the current column. * @param _column_no column number * @param yyscanner The scanner object. */ void yyset_column (int _column_no , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "yyset_column called with no buffer" ); yycolumn = _column_no; } /** Set the input stream. This does not discard the current * input buffer. * @param _in_str A readable stream. * @param yyscanner The scanner object. * @see yy_switch_to_buffer */ void yyset_in (FILE * _in_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyin = _in_str ; } void yyset_out (FILE * _out_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyout = _out_str ; } int yyget_debug (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void yyset_debug (int _bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = _bdebug ; } /* Accessor methods for yylval and yylloc */ YYSTYPE * yyget_lval (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylval; } void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; } YYLTYPE *yyget_lloc (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylloc; } void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylloc = yylloc_param; } /* User-visible API */ /* yylex_init is special because it creates the scanner itself, so it is * the ONLY reentrant function that doesn't take the scanner as the last argument. * That's why we explicitly handle the declaration, instead of using our macros. */ int yylex_init(yyscan_t* ptr_yy_globals) { if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); return yy_init_globals ( *ptr_yy_globals ); } /* yylex_init_extra has the same functionality as yylex_init, but follows the * convention of taking the scanner as the last argument. Note however, that * this is a *pointer* to a scanner, as it will be allocated by this call (and * is the reason, too, why this function also must handle its own declaration). * The user defined value in the first argument will be available to yyalloc in * the yyextra field. */ int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) { struct yyguts_t dummy_yyguts; yyset_extra (yy_user_defined, &dummy_yyguts); if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); yyset_extra (yy_user_defined, *ptr_yy_globals); return yy_init_globals ( *ptr_yy_globals ); } static int yy_init_globals (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Initialization is the same as for the non-reentrant scanner. * This function is called from yylex_destroy(), so don't allocate here. */ yyg->yy_buffer_stack = NULL; yyg->yy_buffer_stack_top = 0; yyg->yy_buffer_stack_max = 0; yyg->yy_c_buf_p = NULL; yyg->yy_init = 0; yyg->yy_start = 0; yyg->yy_start_stack_ptr = 0; yyg->yy_start_stack_depth = 0; yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = NULL; yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* yylex_destroy is for both reentrant and non-reentrant scanners. */ int yylex_destroy (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ yyfree(yyg->yy_buffer_stack , yyscanner); yyg->yy_buffer_stack = NULL; /* Destroy the start condition stack. */ yyfree( yyg->yy_start_stack , yyscanner ); yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (const char * s , yyscan_t yyscanner) { int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yyalloc (yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; return malloc(size); } void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return realloc(ptr, size); } void yyfree (void * ptr , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 182 "lexer.l" void augl_close_lexer(yyscan_t *scanner) { FILE *fp = augl_get_in(scanner); if (fp != NULL) { fclose(fp); augl_set_in(NULL, scanner); } } int augl_init_lexer(struct state *state, yyscan_t *scanner) { FILE *f; struct string *name = state->info->filename; f = fopen(name->str, "r"); if (f == NULL) return -1; if (augl_lex_init(scanner) != 0) { fclose(f); return -1; } augl_set_extra(state, *scanner); augl_set_in(f, *scanner); return 0; } struct info *augl_get_info(yyscan_t scanner) { return augl_get_extra(scanner)->info; } augeas-1.13.0/src/ast.c0000644000175000017500000001417414161102026011504 00000000000000/* * ast.c: Support routines for put/get * * Copyright (C) 2008-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include #include "internal.h" #include "memory.h" #include "lens.h" /* A dictionary that maps key to a list of (skel, dict) */ struct dict_entry { struct dict_entry *next; struct skel *skel; struct dict *dict; }; /* Associates a KEY with a list of skel/dict pairs. Dicts are used in two phases: first they are constructed, through repeated calls to dict_append. In the second phase, items are looked up and removed from the dict. During construction, MARK points to the end of the list ENTRY. Once construction is done, MARK points to the head of that list, and ENTRY is moved towards the tail everytime an item is looked up. */ struct dict_node { char *key; struct dict_entry *entry; /* This will change as entries are looked up */ struct dict_entry *mark; /* Pointer to initial entry, will never change */ }; /* Nodes are kept sorted by their key, with NULL being smaller than any string */ struct dict { struct dict_node **nodes; uint32_t size; uint32_t used; bool marked; }; static const int dict_initial_size = 2; static const int dict_max_expansion = 128; static const uint32_t dict_max_size = (1<<24) - 1; struct dict *make_dict(char *key, struct skel *skel, struct dict *subdict) { struct dict *dict = NULL; if (ALLOC(dict) < 0) goto error; if (ALLOC_N(dict->nodes, dict_initial_size) < 0) goto error; if (ALLOC(dict->nodes[0]) < 0) goto error; if (ALLOC(dict->nodes[0]->entry) < 0) goto error; dict->size = dict_initial_size; dict->used = 1; dict->nodes[0]->key = key; dict->nodes[0]->entry->skel = skel; dict->nodes[0]->entry->dict = subdict; dict->nodes[0]->mark = dict->nodes[0]->entry; return dict; error: if (dict->nodes) { if (dict->nodes[0]) FREE(dict->nodes[0]->entry); FREE(dict->nodes[0]); } FREE(dict->nodes); FREE(dict); return NULL; } void free_dict(struct dict *dict) { if (dict == NULL) return; for (int i=0; i < dict->used; i++) { struct dict_node *node = dict->nodes[i]; if (! dict->marked) node->mark = node->entry; while (node->mark != NULL) { struct dict_entry *del = node->mark; node->mark = del->next; free_skel(del->skel); free_dict(del->dict); free(del); } free(node->key); FREE(node); } FREE(dict->nodes); FREE(dict); } /* Return the position of KEY in DICT as an integer between 0 and DICT->USED. If KEY is not in DICT, return a negative number P such that -(P + 1) is the position at which KEY must be inserted to keep the keys of the nodes in DICT sorted. */ static int dict_pos(struct dict *dict, const char *key) { if (key == NULL) { return (dict->nodes[0]->key == NULL) ? 0 : -1; } int l = dict->nodes[0]->key == NULL ? 1 : 0; int h = dict->used; while (l < h) { int m = (l + h)/2; int cmp = strcmp(dict->nodes[m]->key, key); if (cmp > 0) h = m; else if (cmp < 0) l = m + 1; else return m; } return -(l + 1); } static int dict_expand(struct dict *dict) { uint32_t size = dict->size; if (size == dict_max_size) return -1; if (size > dict_max_expansion) size += dict_max_expansion; else size *= 2; if (size > dict_max_size) size = dict_max_size; dict->size = size; return REALLOC_N(dict->nodes, dict->size); } int dict_append(struct dict **dict, struct dict *d2) { if (d2 == NULL) return 0; if (*dict == NULL) { *dict = d2; return 0; } struct dict *d1 = *dict; for (int i2 = 0; i2 < d2->used; i2++) { struct dict_node *n2 = d2->nodes[i2]; int i1 = dict_pos(d1, n2->key); if (i1 < 0) { i1 = - i1 - 1; if (d1->size == d1->used) { if (dict_expand(d1) < 0) return -1; } memmove(d1->nodes + i1 + 1, d1->nodes + i1, sizeof(*d1->nodes) * (d1->used - i1)); d1->nodes[i1] = n2; d1->used += 1; } else { struct dict_node *n1 = d1->nodes[i1]; list_tail_cons(n1->entry, n1->mark, n2->entry); FREE(n2->key); FREE(n2); } } FREE(d2->nodes); FREE(d2); return 0; } void dict_lookup(const char *key, struct dict *dict, struct skel **skel, struct dict **subdict) { *skel = NULL; *subdict = NULL; if (dict != NULL) { if (! dict->marked) { for (int i=0; i < dict->used; i++) { dict->nodes[i]->mark = dict->nodes[i]->entry; } dict->marked = 1; } int p = dict_pos(dict, key); if (p >= 0) { struct dict_node *node = dict->nodes[p]; if (node->entry != NULL) { *skel = node->entry->skel; *subdict = node->entry->dict; node->entry = node->entry->next; } } } } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/get.c0000644000175000017500000014555214161102026011501 00000000000000/* * parser.c: parse a configuration file according to a grammar * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include #include #include "regexp.h" #include "list.h" #include "internal.h" #include "memory.h" #include "info.h" #include "lens.h" #include "errcode.h" /* Our favorite error message */ static const char *const short_iteration = "Iterated lens matched less than it should"; struct seq { struct seq *next; const char *name; int value; }; struct state { struct info *info; struct span *span; const char *text; struct seq *seqs; char *key; char *value; /* GET_STORE leaves a value here */ struct lns_error *error; int enable_span; /* We use the registers from a regular expression match to keep track * of the substring we are currently looking at. REGS are the registers * from the last regexp match; NREG is the number of the register * in REGS that describes the substring we are currently looking at. * * We adjust NREG as we traverse lenses either because we know the * grouping structure of lenses (for L_STAR, the child lens is always * in NREG + 1) or by looking at the number of groups in a sublens (as * we move from one child of L_CONCAT to the next, we need to add 1 + * number of groups of that child to NREG) How NREG is adjusted is * closely related to how the REGEXP_* routines build up bigger regexps * from smaller ones. */ struct re_registers *regs; uint nreg; }; /* Used by recursive lenses to stack intermediate results Frames are used for a few different purposes: (1) to save results from individual lenses that are later gathered and combined by combinators like L_STAR and L_CONCAT (2) to preserve parse state when descending into a L_SUBTREE (3) as a marker to determine if a L_MAYBE had a match or not */ struct frame { struct lens *lens; char *key; struct span *span; union { struct { /* MGET */ char *value; struct tree *tree; }; struct { /* M_PARSE */ struct skel *skel; struct dict *dict; }; }; }; /* Used by recursive lenses in get_rec and parse_rec */ enum mode_t { M_GET, M_PARSE }; /* Abstract Syntax Tree for recursive parse */ struct ast { struct ast *parent; struct ast **children; uint nchildren; uint capacity; struct lens *lens; uint start; uint end; }; struct rec_state { enum mode_t mode; struct state *state; uint fsize; uint fused; struct frame *frames; size_t start; uint lvl; /* Debug only */ struct ast *ast; /* Will either be get_combine or parse_combine, depending on MODE, for the duration of the whole recursive parse */ void (*combine)(struct rec_state *, struct lens *, uint); }; #define REG_START(state) ((state)->regs->start[(state)->nreg]) #define REG_END(state) ((state)->regs->end[(state)->nreg]) #define REG_SIZE(state) (REG_END(state) - REG_START(state)) #define REG_POS(state) ((state)->text + REG_START(state)) #define REG_VALID(state) ((state)->regs != NULL && \ (state)->nreg < (state)->regs->num_regs) #define REG_MATCHED(state) (REG_VALID(state) \ && (state)->regs->start[(state)->nreg] >= 0) /* The macros SAVE_REGS and RESTORE_REGS are used to remember and restore * where our caller was in processing their regexp match before we do * matching ourselves (and would therefore clobber the caller's state) * * These two macros are admittedly horrible in that they declare local * variables called OLD_REGS and OLD_NREG. The alternative to avoiding that * would be to manually manage a stack of regs/nreg in STATE. */ #define SAVE_REGS(state) \ struct re_registers *old_regs = state->regs; \ uint old_nreg = state->nreg; \ state->regs = NULL; \ state->nreg = 0 #define RESTORE_REGS(state) \ free_regs(state); \ state->regs = old_regs; \ state->nreg = old_nreg; /* * AST utils */ static struct ast *make_ast(struct lens *lens) { struct ast *ast = NULL; if (ALLOC(ast) < 0) return NULL; ast->lens = lens; ast->capacity = 4; if (ALLOC_N(ast->children, ast->capacity) < 0) { FREE(ast); return NULL; } return ast; } /* recursively free the node and all it's descendants */ static void free_ast(struct ast *ast) { int i; if (ast == NULL) return; for (i = 0; i < ast->nchildren; i++) { free_ast(ast->children[i]); } if (ast->children != NULL) FREE(ast->children); FREE(ast); } static struct ast *ast_root(struct ast *ast) { struct ast *root = ast; while(root != NULL && root->parent != NULL) root = root->parent; return root; } static void ast_set(struct ast *ast, uint start, uint end) { if (ast == NULL) return; ast->start = start; ast->end = end; } /* append a child to the parent ast */ static struct ast *ast_append(struct rec_state *rec_state, struct lens *lens, uint start, uint end) { int ret; struct ast *child, *parent; struct state *state = rec_state->state; parent = rec_state->ast; if (parent == NULL) return NULL; child = make_ast(lens); ERR_NOMEM(child == NULL, state->info); ast_set(child, start, end); if (parent->nchildren >= parent->capacity) { ret = REALLOC_N(parent->children, parent->capacity * 2); ERR_NOMEM(ret < 0, state->info); parent->capacity = parent->capacity * 2; } parent->children[parent->nchildren++] = child; child->parent = parent; return child; error: free_ast(child); return NULL; } /* pop the ast from one level, fail the parent is NULL */ static void ast_pop(struct rec_state *rec_state) { ensure(rec_state->ast != NULL && rec_state->ast->parent != NULL, rec_state->state->info); rec_state->ast = rec_state->ast->parent; error: return; } static void print_ast(const struct ast *ast, int lvl) { int i; char *lns; if (ast == NULL) return; for (i = 0; i < lvl; i++) fputs(" ", stdout); lns = format_lens(ast->lens); printf("%d..%d %s\n", ast->start, ast->end, lns); free(lns); for (i = 0; i < ast->nchildren; i++) { print_ast(ast->children[i], lvl + 1); } } static void get_error(struct state *state, struct lens *lens, const char *format, ...) ATTRIBUTE_FORMAT(printf, 3, 4); void free_lns_error(struct lns_error *err) { if (err == NULL) return; free(err->message); free(err->path); unref(err->lens, lens); free(err); } static void vget_error(struct state *state, struct lens *lens, const char *format, va_list ap) { int r; if (state->error != NULL) return; if (ALLOC(state->error) < 0) return; state->error->lens = ref(lens); if (REG_MATCHED(state)) state->error->pos = REG_END(state); else state->error->pos = 0; r = vasprintf(&state->error->message, format, ap); if (r == -1) state->error->message = NULL; } static void get_error(struct state *state, struct lens *lens, const char *format, ...) { va_list ap; va_start(ap, format); vget_error(state, lens, format, ap); va_end(ap); } static struct skel *make_skel(struct lens *lens) { struct skel *skel; enum lens_tag tag = lens->tag; if (ALLOC(skel) < 0) return NULL; skel->tag = tag; return skel; } void free_skel(struct skel *skel) { if (skel == NULL) return; if (skel->tag == L_CONCAT || skel->tag == L_STAR || skel->tag == L_MAYBE || skel->tag == L_SQUARE) { while (skel->skels != NULL) { struct skel *del = skel->skels; skel->skels = del->next; free_skel(del); } } else if (skel->tag == L_DEL) { free(skel->text); } free(skel); } static void print_skel(struct skel *skel); static void print_skel_list(struct skel *skels, const char *beg, const char *sep, const char *end) { printf("%s", beg); list_for_each(s, skels) { print_skel(s); if (s->next != NULL) printf("%s", sep); } printf("%s", end); } static void print_skel(struct skel *skel) { switch(skel->tag) { case L_DEL: if (skel->text == NULL) { printf("<>"); } else { fputc('\'', stdout); print_chars(stdout, skel->text, -1); fputc('\'', stdout); } break; case L_CONCAT: print_skel_list(skel->skels, "", " . ", ""); break; case L_STAR: print_skel_list(skel->skels, "(", " ", ")*"); break; case L_MAYBE: print_skel_list(skel->skels, "(", " ", ")?"); break; case L_SUBTREE: print_skel_list(skel->skels, "[", " ", "]"); break; default: printf("??"); break; } } // DICT_DUMP is only used for debugging #ifdef DICT_DUMP static void print_dict(struct dict *dict, int indent) { list_for_each(d, dict) { printf("%*s%s:\n", indent, "", d->key); list_for_each(e, d->entry) { printf("%*s", indent+2, ""); print_skel(e->skel); printf("\n"); print_dict(e->dict, indent+2); } } } #endif static void get_expected_error(struct state *state, struct lens *l) { /* Size of the excerpt of the input text we'll show */ static const int wordlen = 10; char word[wordlen+1]; char *p, *pat; if (REG_MATCHED(state)) strncpy(word, REG_POS(state), wordlen); else strncpy(word, state->text, wordlen); word[wordlen] = '\0'; for (p = word; *p != '\0' && *p != '\n'; p++); *p = '\0'; pat = escape(l->ctype->pattern->str, -1, NULL); get_error(state, l, "expected %s at '%s'", pat, word); free(pat); } /* * Splitting of the input text */ static char *token(struct state *state) { ensure0(REG_MATCHED(state), state->info); return strndup(REG_POS(state), REG_SIZE(state)); } static char *token_range(const char *text, uint start, uint end) { return strndup(text + start, end - start); } static void regexp_match_error(struct state *state, struct lens *lens, int count, struct regexp *r) { char *text = NULL; char *pat = regexp_escape(r); if (state->regs != NULL) text = strndup(REG_POS(state), REG_SIZE(state)); else text = strdup("(unknown)"); if (count == -1) { get_error(state, lens, "Failed to match /%s/ with %s", pat, text); } else if (count == -2) { get_error(state, lens, "Internal error matching /%s/ with %s", pat, text); } else if (count == -3) { /* Should have been caught by the typechecker */ get_error(state, lens, "Syntax error in regexp /%s/", pat); } free(pat); free(text); } static void no_match_error(struct state *state, struct lens *lens) { ensure(lens->tag == L_KEY || lens->tag == L_DEL || lens->tag == L_STORE, state->info); char *pat = regexp_escape(lens->ctype); const char *lname = "(lname)"; if (lens->tag == L_KEY) lname = "key"; else if (lens->tag == L_DEL) lname = "del"; else if (lens->tag == L_STORE) lname = "store"; get_error(state, lens, "no match for %s /%s/", lname, pat); free(pat); error: return; } /* Modifies STATE->REGS and STATE->NREG. The caller must save these * if they are still needed * * Return the number of characters matched */ static int match(struct state *state, struct lens *lens, struct regexp *re, uint size, uint start) { struct re_registers *regs; int count; if (ALLOC(regs) < 0) return -1; count = regexp_match(re, state->text, size, start, regs); if (count < -1) { regexp_match_error(state, lens, count, re); FREE(regs); return -1; } state->regs = regs; state->nreg = 0; return count; } static void free_regs(struct state *state) { if (state->regs != NULL) { free(state->regs->start); free(state->regs->end); FREE(state->regs); } } static struct tree *get_lens(struct lens *lens, struct state *state); static struct skel *parse_lens(struct lens *lens, struct state *state, struct dict **dict); static void free_seqs(struct seq *seqs) { /* Do not free seq->name; it's not owned by the seq, but by some lens */ list_free(seqs); } static struct seq *find_seq(const char *name, struct state *state) { ensure0(name != NULL, state->info); struct seq *seq; for (seq=state->seqs; seq != NULL && STRNEQ(seq->name, name); seq = seq->next); if (seq == NULL) { if (ALLOC(seq) < 0) return NULL; seq->name = name; seq->value = 1; list_append(state->seqs, seq); } return seq; } static struct tree *get_seq(struct lens *lens, struct state *state) { ensure0(lens->tag == L_SEQ, state->info); struct seq *seq = find_seq(lens->string->str, state); int r; r = asprintf((char **) &(state->key), "%d", seq->value); ERR_NOMEM(r < 0, state->info); seq->value += 1; error: return NULL; } static struct skel *parse_seq(struct lens *lens, struct state *state) { get_seq(lens, state); return make_skel(lens); } static struct tree *get_counter(struct lens *lens, struct state *state) { ensure0(lens->tag == L_COUNTER, state->info); struct seq *seq = find_seq(lens->string->str, state); seq->value = 1; return NULL; } static struct skel *parse_counter(struct lens *lens, struct state *state) { get_counter(lens, state); return make_skel(lens); } static struct tree *get_del(struct lens *lens, struct state *state) { ensure0(lens->tag == L_DEL, state->info); if (! REG_MATCHED(state)) { char *pat = regexp_escape(lens->ctype); get_error(state, lens, "no match for del /%s/", pat); free(pat); } update_span(state->span, REG_START(state), REG_END(state)); return NULL; } static struct skel *parse_del(struct lens *lens, struct state *state) { ensure0(lens->tag == L_DEL, state->info); struct skel *skel = NULL; skel = make_skel(lens); if (! REG_MATCHED(state)) no_match_error(state, lens); else skel->text = token(state); return skel; } static struct tree *get_store(struct lens *lens, struct state *state) { ensure0(lens->tag == L_STORE, state->info); ensure0(state->value == NULL, state->info); struct tree *tree = NULL; if (state->value != NULL) get_error(state, lens, "More than one store in a subtree"); else if (! REG_MATCHED(state)) no_match_error(state, lens); else { state->value = token(state); if (state->span) { state->span->value_start = REG_START(state); state->span->value_end = REG_END(state); update_span(state->span, REG_START(state), REG_END(state)); } } return tree; } static struct skel *parse_store(struct lens *lens, ATTRIBUTE_UNUSED struct state *state) { ensure0(lens->tag == L_STORE, state->info); return make_skel(lens); } static struct tree *get_value(struct lens *lens, struct state *state) { ensure0(lens->tag == L_VALUE, state->info); state->value = strdup(lens->string->str); return NULL; } static struct skel *parse_value(struct lens *lens, ATTRIBUTE_UNUSED struct state *state) { ensure0(lens->tag == L_VALUE, state->info); return make_skel(lens); } static struct tree *get_key(struct lens *lens, struct state *state) { ensure0(lens->tag == L_KEY, state->info); if (! REG_MATCHED(state)) no_match_error(state, lens); else { state->key = token(state); if (state->span) { state->span->label_start = REG_START(state); state->span->label_end = REG_END(state); update_span(state->span, REG_START(state), REG_END(state)); } } return NULL; } static struct skel *parse_key(struct lens *lens, struct state *state) { get_key(lens, state); return make_skel(lens); } static struct tree *get_label(struct lens *lens, struct state *state) { ensure0(lens->tag == L_LABEL, state->info); state->key = strdup(lens->string->str); return NULL; } static struct skel *parse_label(struct lens *lens, struct state *state) { get_label(lens, state); return make_skel(lens); } static struct tree *get_union(struct lens *lens, struct state *state) { ensure0(lens->tag == L_UNION, state->info); struct tree *tree = NULL; int applied = 0; uint old_nreg = state->nreg; state->nreg += 1; for (int i=0; i < lens->nchildren; i++) { if (REG_MATCHED(state)) { tree = get_lens(lens->children[i], state); applied = 1; break; } state->nreg += 1 + regexp_nsub(lens->children[i]->ctype); } state->nreg = old_nreg; if (!applied) get_expected_error(state, lens); return tree; } static struct skel *parse_union(struct lens *lens, struct state *state, struct dict **dict) { ensure0(lens->tag == L_UNION, state->info); struct skel *skel = NULL; int applied = 0; uint old_nreg = state->nreg; state->nreg += 1; for (int i=0; i < lens->nchildren; i++) { struct lens *l = lens->children[i]; if (REG_MATCHED(state)) { skel = parse_lens(l, state, dict); applied = 1; break; } state->nreg += 1 + regexp_nsub(lens->children[i]->ctype); } state->nreg = old_nreg; if (! applied) get_expected_error(state, lens); return skel; } static struct tree *get_concat(struct lens *lens, struct state *state) { ensure0(lens->tag == L_CONCAT, state->info); struct tree *tree = NULL; uint old_nreg = state->nreg; state->nreg += 1; for (int i=0; i < lens->nchildren; i++) { struct tree *t = NULL; if (! REG_VALID(state)) { get_error(state, lens->children[i], "Not enough components in concat"); free_tree(tree); state->nreg = old_nreg; return NULL; } t = get_lens(lens->children[i], state); list_append(tree, t); state->nreg += 1 + regexp_nsub(lens->children[i]->ctype); } state->nreg = old_nreg; return tree; } static struct skel *parse_concat(struct lens *lens, struct state *state, struct dict **dict) { ensure0(lens->tag == L_CONCAT, state->info); struct skel *skel = make_skel(lens); uint old_nreg = state->nreg; state->nreg += 1; for (int i=0; i < lens->nchildren; i++) { struct skel *sk = NULL; struct dict *di = NULL; if (! REG_VALID(state)) { get_error(state, lens->children[i], "Not enough components in concat"); free_skel(skel); return NULL; } sk = parse_lens(lens->children[i], state, &di); list_append(skel->skels, sk); dict_append(dict, di); state->nreg += 1 + regexp_nsub(lens->children[i]->ctype); } state->nreg = old_nreg; return skel; } /* Given a lens that does not match at the current position, try to find * the left-most child LAST that does match and the lens next to it NEXT * that does not match. Return the length of the match. * * If no such child exists, return 0 */ static int try_match(struct lens *lens, struct state *state, uint start, uint end, struct lens **last, struct lens **next) { int result = 0, r; switch(lens->tag) { case L_VALUE: case L_LABEL: case L_SEQ: case L_COUNTER: *last = lens; return result; break; case L_DEL: case L_KEY: case L_STORE: result = regexp_match(lens->ctype, state->text, end, start, NULL); if (result >= 0) *last = lens; return result; case L_CONCAT: for (int i=0; i < lens->nchildren; i++) { struct lens *child = lens->children[i]; struct lens *next_child = (i < lens->nchildren - 1) ? lens->children[i+1] : NULL; r = regexp_match(child->ctype, state->text, end, start, NULL); if (r >= 0) { result += r; start += r; *last = child; } else if (result > 0) { if (*next == NULL) *next = child; return result; } else { result = try_match(child, state, start, end, last, next); if (result > 0 && *next == NULL) *next = next_child; return result; } } return result; break; case L_UNION: for (int i=0; i < lens->nchildren; i++) { struct lens *child = lens->children[i]; result = try_match(child, state, start, end, last, next); if (result > 0) return result; } return 0; break; case L_SUBTREE: case L_STAR: case L_MAYBE: case L_SQUARE: return try_match(lens->child, state, start, end, last, next); break; default: BUG_ON(true, state->info, "illegal lens tag %d", lens->tag); break; } error: return 0; } static void short_iteration_error(struct lens *lens, struct state *state, uint start, uint end) { int match_count; get_error(state, lens, "%s", short_iteration); match_count = try_match(lens->child, state, start, end, &state->error->last, &state->error->next); state->error->pos = start + match_count; } static struct tree *get_quant_star(struct lens *lens, struct state *state) { ensure0(lens->tag == L_STAR, state->info); struct lens *child = lens->child; struct tree *tree = NULL, *tail = NULL; uint end = REG_END(state); uint start = REG_START(state); uint size = end - start; SAVE_REGS(state); while (size > 0 && match(state, child, child->ctype, end, start) > 0) { struct tree *t = NULL; t = get_lens(lens->child, state); list_tail_cons(tree, tail, t); start += REG_SIZE(state); size -= REG_SIZE(state); free_regs(state); } RESTORE_REGS(state); if (size != 0) { short_iteration_error(lens, state, start, end); } return tree; } static struct skel *parse_quant_star(struct lens *lens, struct state *state, struct dict **dict) { ensure0(lens->tag == L_STAR, state->info); struct lens *child = lens->child; struct skel *skel = make_skel(lens), *tail = NULL; uint end = REG_END(state); uint start = REG_START(state); uint size = end - start; *dict = NULL; SAVE_REGS(state); while (size > 0 && match(state, child, child->ctype, end, start) > 0) { struct skel *sk; struct dict *di = NULL; sk = parse_lens(lens->child, state, &di); list_tail_cons(skel->skels, tail, sk); dict_append(dict, di); start += REG_SIZE(state); size -= REG_SIZE(state); free_regs(state); } RESTORE_REGS(state); if (size != 0) { get_error(state, lens, "%s", short_iteration); } return skel; } static struct tree *get_quant_maybe(struct lens *lens, struct state *state) { ensure0(lens->tag == L_MAYBE, state->info); struct tree *tree = NULL; /* Check that our child matched. For a construct like (r)?, the * matcher will report a zero length match for group 0, even if r * does not match at all */ state->nreg += 1; if (REG_MATCHED(state)) { tree = get_lens(lens->child, state); } state->nreg -= 1; return tree; } static struct skel *parse_quant_maybe(struct lens *lens, struct state *state, struct dict **dict) { ensure0(lens->tag == L_MAYBE, state->info); struct skel *skel = NULL; state->nreg += 1; if (REG_MATCHED(state)) { skel = parse_lens(lens->child, state, dict); } state->nreg -= 1; if (skel == NULL) skel = make_skel(lens); return skel; } static struct tree *get_subtree(struct lens *lens, struct state *state) { char *key = state->key; char *value = state->value; struct span *span = move(state->span); struct tree *tree = NULL, *children; state->key = NULL; state->value = NULL; if (state->enable_span) { state->span = make_span(state->info); ERR_NOMEM(state->span == NULL, state->info); } children = get_lens(lens->child, state); tree = make_tree(state->key, state->value, NULL, children); ERR_NOMEM(tree == NULL, state->info); tree->span = move(state->span); if (tree->span != NULL) { update_span(span, tree->span->span_start, tree->span->span_end); } state->key = key; state->value = value; state->span = span; return tree; error: free_span(state->span); state->span = span; return NULL; } static struct skel *parse_subtree(struct lens *lens, struct state *state, struct dict **dict) { char *key = state->key; struct skel *skel; struct dict *di = NULL; state->key = NULL; skel = parse_lens(lens->child, state, &di); *dict = make_dict(state->key, skel, di); state->key = key; return make_skel(lens); } /* Check if left and right strings matches according to the square lens * definition. * * Returns 1 if strings matches, 0 otherwise */ static int square_match(struct lens *lens, char *left, char *right) { int cmp = 0; struct lens *concat = NULL; // if one of the argument is NULL, returns no match if (left == NULL || right == NULL || lens == NULL) return cmp; concat = lens->child; /* If either right or left lens is nocase, then ignore case */ if (child_first(concat)->ctype->nocase || child_last(concat)->ctype->nocase) { cmp = STRCASEEQ(left, right); } else { cmp = STREQ(left, right); } return cmp; } /* * This function applies only for non-recursive lens, handling of recursive * square is done in visit_exit(). */ static struct tree *get_square(struct lens *lens, struct state *state) { ensure0(lens->tag == L_SQUARE, state->info); struct lens *concat = lens->child; struct tree *tree = NULL; uint end = REG_END(state); uint start = REG_START(state); char *rsqr = NULL, *lsqr = NULL; int r; SAVE_REGS(state); r = match(state, lens->child, lens->child->ctype, end, start); ERR_NOMEM(r < 0, state->info); tree = get_lens(lens->child, state); /* retrieve left component */ state->nreg = 1; start = REG_START(state); end = REG_END(state); lsqr = token_range(state->text, start, end); /* retrieve right component */ /* compute nreg for the last children */ for (int i = 0; i < concat->nchildren - 1; i++) state->nreg += 1 + regexp_nsub(concat->children[i]->ctype); start = REG_START(state); end = REG_END(state); rsqr = token_range(state->text, start, end); if (!square_match(lens, lsqr, rsqr)) { get_error(state, lens, "%s \"%s\" %s \"%s\"", "Parse error: mismatched in square lens, expecting", lsqr, "but got", rsqr); goto error; } done: RESTORE_REGS(state); FREE(lsqr); FREE(rsqr); return tree; error: free_tree(tree); tree = NULL; goto done; } static struct skel *parse_square(struct lens *lens, struct state *state, struct dict **dict) { ensure0(lens->tag == L_SQUARE, state->info); uint end = REG_END(state); uint start = REG_START(state); struct skel *skel = NULL, *sk = NULL; int r; SAVE_REGS(state); r = match(state, lens->child, lens->child->ctype, end, start); ERR_NOMEM(r < 0, state->info); skel = parse_lens(lens->child, state, dict); if (skel == NULL) return NULL; sk = make_skel(lens); sk->skels = skel; error: RESTORE_REGS(state); return sk; } /* * Helpers for recursive lenses */ ATTRIBUTE_UNUSED static void print_frames(struct rec_state *state) { for (int j = state->fused - 1; j >=0; j--) { struct frame *f = state->frames + j; for (int i=0; i < state->lvl; i++) fputc(' ', stderr); fprintf(stderr, "%2d %s %s", j, f->key, f->value); if (f->tree == NULL) { fprintf(stderr, " - "); } else { fprintf(stderr, " { %s = %s } ", f->tree->label, f->tree->value); } char *s = format_lens(f->lens); if (s != NULL) { fprintf(stderr, "%s\n", s); free(s); } } } ATTRIBUTE_PURE static struct frame *top_frame(struct rec_state *state) { ensure0(state->fsize > 0, state->state->info); return state->frames + state->fused - 1; } /* The nth frame from the top of the stack, where 0th frame is the top */ ATTRIBUTE_PURE static struct frame *nth_frame(struct rec_state *state, uint n) { ensure0(state->fsize > n, state->state->info); return state->frames + state->fused - (n+1); } static struct frame *push_frame(struct rec_state *state, struct lens *lens) { int r; if (state->fused >= state->fsize) { uint expand = state->fsize; if (expand < 8) expand = 8; r = REALLOC_N(state->frames, state->fsize + expand); ERR_NOMEM(r < 0, state->state->info); state->fsize += expand; } state->fused += 1; struct frame *top = top_frame(state); MEMZERO(top, 1); top->lens = lens; return top; error: return NULL; } static struct frame *pop_frame(struct rec_state *state) { ensure0(state->fused > 0, state->state->info); struct frame *result = top_frame(state); state->fused -= 1; return result; } static void dbg_visit(struct lens *lens, char action, size_t start, size_t end, int fused, int lvl) { char *lns; for (int i=0; i < lvl; i++) fputc(' ', stderr); lns = format_lens(lens); fprintf(stderr, "%c %zd..%zd %d %s\n", action, start, end, fused, lns); free(lns); } static void get_terminal(struct frame *top, struct lens *lens, struct state *state) { top->tree = get_lens(lens, state); top->key = state->key; top->value = state->value; state->key = NULL; state->value = NULL; } static void parse_terminal(struct frame *top, struct lens *lens, struct state *state) { top->dict = NULL; top->skel = parse_lens(lens, state, &top->dict); top->key = state->key; state->key = NULL; } static void visit_terminal(struct lens *lens, size_t start, size_t end, void *data) { struct rec_state *rec_state = data; struct state *state = rec_state->state; struct ast *child; if (state->error != NULL) return; SAVE_REGS(state); if (debugging("cf.get")) dbg_visit(lens, 'T', start, end, rec_state->fused, rec_state->lvl); match(state, lens, lens->ctype, end, start); struct frame *top = push_frame(rec_state, lens); ERR_BAIL(state->info); if (rec_state->mode == M_GET) get_terminal(top, lens, state); else parse_terminal(top, lens, state); child = ast_append(rec_state, lens, start, end); ERR_NOMEM(child == NULL, state->info); error: RESTORE_REGS(state); } static bool rec_gen_span(struct rec_state *rec_state) { return ((rec_state->mode == M_GET) && (rec_state->state->enable_span)); } static void visit_enter(struct lens *lens, ATTRIBUTE_UNUSED size_t start, ATTRIBUTE_UNUSED size_t end, void *data) { struct rec_state *rec_state = data; struct state *state = rec_state->state; struct ast *child; if (state->error != NULL) return; if (debugging("cf.get")) dbg_visit(lens, '{', start, end, rec_state->fused, rec_state->lvl); rec_state->lvl += 1; if (lens->tag == L_SUBTREE) { /* Same for parse and get */ /* Use this frame to preserve the current state before we process the contents of the subtree, i.e., lens->child */ struct frame *f = push_frame(rec_state, lens); ERR_BAIL(state->info); f->key = state->key; f->value = state->value; state->key = NULL; state->value = NULL; if (rec_gen_span(rec_state)) { f->span = state->span; state->span = make_span(state->info); ERR_NOMEM(state->span == NULL, state->info); } } else if (lens->tag == L_MAYBE) { /* Push a frame as a marker so we can tell whether lens->child actually had a match or not */ push_frame(rec_state, lens); ERR_BAIL(state->info); } child = ast_append(rec_state, lens, start, end); if (child != NULL) rec_state->ast = child; error: return; } /* Combine n frames from the stack into one new frame for M_GET */ static void get_combine(struct rec_state *rec_state, struct lens *lens, uint n) { struct tree *tree = NULL, *tail = NULL; char *key = NULL, *value = NULL; struct frame *top = NULL; for (int i=0; i < n; i++) { top = pop_frame(rec_state); ERR_BAIL(lens->info); list_tail_cons(tree, tail, top->tree); /* top->tree might have more than one node, update tail */ if (tail != NULL) while (tail->next != NULL) tail = tail->next; if (top->key != NULL) { ensure(key == NULL, rec_state->state->info); key = top->key; } if (top->value != NULL) { ensure(value == NULL, rec_state->state->info); value = top->value; } } top = push_frame(rec_state, lens); ERR_BAIL(lens->info); top->tree = tree; top->key = key; top->value = value; error: return; } /* Combine n frames from the stack into one new frame for M_PUT */ static void parse_combine(struct rec_state *rec_state, struct lens *lens, uint n) { struct skel *skel = make_skel(lens), *tail = NULL; struct dict *dict = NULL; char *key = NULL; struct frame *top = NULL; for (int i=0; i < n; i++) { top = pop_frame(rec_state); ERR_BAIL(lens->info); list_tail_cons(skel->skels, tail, top->skel); /* top->skel might have more than one node, update skel */ if (tail != NULL) while (tail->next != NULL) tail = tail->next; dict_append(&dict, top->dict); if (top->key != NULL) { ensure(key == NULL, rec_state->state->info); key = top->key; } } top = push_frame(rec_state, lens); ERR_BAIL(lens->info); top->skel = move(skel); top->dict = move(dict); top->key = key; error: free_skel(skel); free_dict(dict); return; } static void visit_exit_put_subtree(struct lens *lens, struct rec_state *rec_state, struct frame *top) { struct state *state = rec_state->state; struct skel *skel = NULL; struct dict *dict = NULL; skel = make_skel(lens); ERR_NOMEM(skel == NULL, lens->info); dict = make_dict(top->key, top->skel, top->dict); ERR_NOMEM(dict == NULL, lens->info); top = pop_frame(rec_state); ERR_BAIL(state->info); ensure(lens == top->lens, state->info); state->key = top->key; top = push_frame(rec_state, lens); ERR_BAIL(state->info); top->skel = move(skel); top->dict = move(dict); error: free_skel(skel); free_dict(dict); } static void visit_exit(struct lens *lens, ATTRIBUTE_UNUSED size_t start, ATTRIBUTE_UNUSED size_t end, void *data) { struct rec_state *rec_state = data; struct state *state = rec_state->state; struct tree *tree = NULL; if (state->error != NULL) return; rec_state->lvl -= 1; if (debugging("cf.get")) dbg_visit(lens, '}', start, end, rec_state->fused, rec_state->lvl); ERR_BAIL(lens->info); if (lens->tag == L_SUBTREE) { /* Get the result of parsing lens->child */ struct frame *top = pop_frame(rec_state); ERR_BAIL(state->info); if (rec_state->mode == M_GET) { tree = make_tree(top->key, top->value, NULL, top->tree); ERR_NOMEM(tree == NULL, lens->info); tree->span = state->span; /* Restore the parse state from before entering this subtree */ top = pop_frame(rec_state); ERR_BAIL(state->info); ensure(lens == top->lens, state->info); state->key = top->key; state->value = top->value; state->span = top->span; /* Push the result of parsing this subtree */ top = push_frame(rec_state, lens); ERR_BAIL(state->info); top->tree = move(tree); } else { visit_exit_put_subtree(lens, rec_state, top); } } else if (lens->tag == L_CONCAT) { ensure(rec_state->fused >= lens->nchildren, state->info); for (int i = 0; i < lens->nchildren; i++) { struct frame *fr = nth_frame(rec_state, i); ERR_BAIL(state->info); BUG_ON(lens->children[i] != fr->lens, lens->info, "Unexpected lens in concat %zd..%zd\n Expected: %s\n Actual: %s", start, end, format_lens(lens->children[i]), format_lens(fr->lens)); } rec_state->combine(rec_state, lens, lens->nchildren); } else if (lens->tag == L_STAR) { uint n = 0; while (n < rec_state->fused && nth_frame(rec_state, n)->lens == lens->child) n++; ERR_BAIL(state->info); rec_state->combine(rec_state, lens, n); } else if (lens->tag == L_MAYBE) { uint n = 1; if (rec_state->fused > 0 && top_frame(rec_state)->lens == lens->child) { n = 2; } ERR_BAIL(state->info); /* If n = 2, the top of the stack is our child's result, and the frame underneath it is the marker frame we pushed during visit_enter. Combine these two frames into one, which represents the result of parsing the whole L_MAYBE. */ rec_state->combine(rec_state, lens, n); } else if (lens->tag == L_SQUARE) { if (rec_state->mode == M_GET) { struct ast *square, *concat, *right, *left; char *rsqr, *lsqr; int ret; square = rec_state->ast; concat = child_first(square); right = child_first(concat); left = child_last(concat); lsqr = token_range(state->text, left->start, left->end); rsqr = token_range(state->text, right->start, right->end); ret = square_match(lens, lsqr, rsqr); if (! ret) { get_error(state, lens, "%s \"%s\" %s \"%s\"", "Parse error: mismatched in square lens, expecting", lsqr, "but got", rsqr); } FREE(lsqr); FREE(rsqr); if (! ret) goto error; } rec_state->combine(rec_state, lens, 1); } else { /* Turn the top frame from having the result of one of our children to being our result */ top_frame(rec_state)->lens = lens; ERR_BAIL(state->info); } ast_pop(rec_state); error: free_tree(tree); return; } static void visit_error(struct lens *lens, void *data, size_t pos, const char *format, ...) { struct rec_state *rec_state = data; va_list ap; va_start(ap, format); vget_error(rec_state->state, lens, format, ap); va_end(ap); rec_state->state->error->pos = rec_state->start + pos; } static struct frame *rec_process(enum mode_t mode, struct lens *lens, struct state *state) { uint end = REG_END(state); uint start = REG_START(state); size_t len = 0; int r; struct jmt_visitor visitor; struct rec_state rec_state; int i; struct frame *f = NULL; MEMZERO(&rec_state, 1); MEMZERO(&visitor, 1); SAVE_REGS(state); if (lens->jmt == NULL) { lens->jmt = jmt_build(lens); ERR_BAIL(lens->info); } rec_state.mode = mode; rec_state.state = state; rec_state.fused = 0; rec_state.lvl = 0; rec_state.start = start; rec_state.ast = make_ast(lens); rec_state.combine = (mode == M_GET) ? get_combine : parse_combine; ERR_NOMEM(rec_state.ast == NULL, state->info); visitor.parse = jmt_parse(lens->jmt, state->text + start, end - start); ERR_BAIL(lens->info); visitor.terminal = visit_terminal; visitor.enter = visit_enter; visitor.exit = visit_exit; visitor.error = visit_error; visitor.data = &rec_state; r = jmt_visit(&visitor, &len); ERR_BAIL(lens->info); if (r != 1) { get_error(state, lens, "Syntax error"); state->error->pos = start + len; } if (rec_state.fused == 0) { get_error(state, lens, "Parse did not leave a result on the stack"); goto error; } else if (rec_state.fused > 1) { get_error(state, lens, "Parse left additional garbage on the stack"); goto error; } rec_state.ast = ast_root(rec_state.ast); ensure(rec_state.ast->parent == NULL, state->info); done: if (debugging("cf.get.ast")) print_ast(ast_root(rec_state.ast), 0); RESTORE_REGS(state); jmt_free_parse(visitor.parse); free_ast(ast_root(rec_state.ast)); return rec_state.frames; error: for(i = 0; i < rec_state.fused; i++) { f = nth_frame(&rec_state, i); FREE(f->key); free_span(f->span); if (mode == M_GET) { FREE(f->value); free_tree(f->tree); } else if (mode == M_PARSE) { free_skel(f->skel); free_dict(f->dict); } } FREE(rec_state.frames); goto done; } static struct tree *get_rec(struct lens *lens, struct state *state) { struct frame *fr; struct tree *tree = NULL; fr = rec_process(M_GET, lens, state); if (fr != NULL) { tree = fr->tree; state->key = fr->key; state->value = fr->value; FREE(fr); } return tree; } static struct skel *parse_rec(struct lens *lens, struct state *state, struct dict **dict) { struct skel *skel = NULL; struct frame *fr; fr = rec_process(M_PARSE, lens, state); if (fr != NULL) { skel = fr->skel; *dict = fr->dict; state->key = fr->key; FREE(fr); } return skel; } static struct tree *get_lens(struct lens *lens, struct state *state) { struct tree *tree = NULL; switch(lens->tag) { case L_DEL: tree = get_del(lens, state); break; case L_STORE: tree = get_store(lens, state); break; case L_VALUE: tree = get_value(lens, state); break; case L_KEY: tree = get_key(lens, state); break; case L_LABEL: tree = get_label(lens, state); break; case L_SEQ: tree = get_seq(lens, state); break; case L_COUNTER: tree = get_counter(lens, state); break; case L_CONCAT: tree = get_concat(lens, state); break; case L_UNION: tree = get_union(lens, state); break; case L_SUBTREE: tree = get_subtree(lens, state); break; case L_STAR: tree = get_quant_star(lens, state); break; case L_MAYBE: tree = get_quant_maybe(lens, state); break; case L_SQUARE: tree = get_square(lens, state); break; default: BUG_ON(true, state->info, "illegal lens tag %d", lens->tag); break; } error: return tree; } /* Initialize registers. Return 0 if the lens matches the entire text, 1 if * it does not and -1 on error. */ static int init_regs(struct state *state, struct lens *lens, uint size) { int r; if (lens->tag != L_STAR && ! lens->recursive) { r = match(state, lens, lens->ctype, size, 0); if (r == -1) get_error(state, lens, "Input string does not match at all"); if (r <= -1) return -1; return r != size; } /* Special case the very common situation that the lens is (l)* * We can avoid matching the entire text in that case - that * match can be very expensive */ if (ALLOC(state->regs) < 0) return -1; state->regs->num_regs = 1; if (ALLOC(state->regs->start) < 0 || ALLOC(state->regs->end) < 0) return -1; state->regs->start[0] = 0; state->regs->end[0] = size; return 0; } struct tree *lns_get(struct info *info, struct lens *lens, const char *text, int enable_span, struct lns_error **err) { struct state state; struct tree *tree = NULL; uint size = strlen(text); int partial, r; MEMZERO(&state, 1); r = ALLOC(state.info); ERR_NOMEM(r < 0, info); *state.info = *info; state.info->ref = UINT_MAX; state.text = text; state.enable_span = enable_span; /* We are probably being overly cautious here: if the lens can't process * all of TEXT, we should really fail somewhere in one of the sublenses. * But to be safe, we check that we can process everything anyway, then * try to process, hoping we'll get a more specific error, and if that * fails, we throw our arms in the air and say 'something went wrong' */ partial = init_regs(&state, lens, size); if (partial >= 0) { if (lens->recursive) tree = get_rec(lens, &state); else tree = get_lens(lens, &state); } free_seqs(state.seqs); if (state.key != NULL) { get_error(&state, lens, "get left unused key %s", state.key); free(state.key); } if (state.value != NULL) { get_error(&state, lens, "get left unused value %s", state.value); free(state.value); } if (partial && state.error == NULL) { get_error(&state, lens, "Get did not match entire input"); } error: free_regs(&state); FREE(state.info); if (err != NULL) { *err = state.error; } else { if (state.error != NULL) { free_tree(tree); tree = NULL; } free_lns_error(state.error); } return tree; } static struct skel *parse_lens(struct lens *lens, struct state *state, struct dict **dict) { struct skel *skel = NULL; switch(lens->tag) { case L_DEL: skel = parse_del(lens, state); break; case L_STORE: skel = parse_store(lens, state); break; case L_VALUE: skel = parse_value(lens, state); break; case L_KEY: skel = parse_key(lens, state); break; case L_LABEL: skel = parse_label(lens, state); break; case L_SEQ: skel = parse_seq(lens, state); break; case L_COUNTER: skel = parse_counter(lens, state); break; case L_CONCAT: skel = parse_concat(lens, state, dict); break; case L_UNION: skel = parse_union(lens, state, dict); break; case L_SUBTREE: skel = parse_subtree(lens, state, dict); break; case L_STAR: skel = parse_quant_star(lens, state, dict); break; case L_MAYBE: skel = parse_quant_maybe(lens, state, dict); break; case L_SQUARE: skel = parse_square(lens, state, dict); break; default: BUG_ON(true, state->info, "illegal lens tag %d", lens->tag); break; } error: return skel; } struct skel *lns_parse(struct lens *lens, const char *text, struct dict **dict, struct lns_error **err) { struct state state; struct skel *skel = NULL; uint size = strlen(text); int partial, r; MEMZERO(&state, 1); r = ALLOC(state.info); ERR_NOMEM(r< 0, lens->info); state.info->ref = UINT_MAX; state.info->error = lens->info->error; state.text = text; state.text = text; partial = init_regs(&state, lens, size); if (! partial) { *dict = NULL; if (lens->recursive) skel = parse_rec(lens, &state, dict); else skel = parse_lens(lens, &state, dict); free_seqs(state.seqs); if (state.error != NULL) { free_skel(skel); skel = NULL; free_dict(*dict); *dict = NULL; } if (state.key != NULL) { get_error(&state, lens, "parse left unused key %s", state.key); free(state.key); } if (state.value != NULL) { get_error(&state, lens, "parse left unused value %s", state.value); free(state.value); } } else { // This should never happen during lns_parse get_error(&state, lens, "parse can not process entire input"); } error: free_regs(&state); FREE(state.info); if (err != NULL) { *err = state.error; } else { free_lns_error(state.error); } return skel; } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/pathx.c0000644000175000017500000025702514161102026012045 00000000000000/* * pathx.c: handling path expressions * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include #include #include #include #include #include "ref.h" #include "regexp.h" #include "errcode.h" static const char *const errcodes[] = { "no error", "empty name", "illegal string literal", "illegal number", /* PATHX_ENUMBER */ "string missing ending ' or \"", "expected '='", "allocation failed", "unmatched '['", "unmatched '('", "expected a '/'", "internal error", /* PATHX_EINTERNAL */ "type error", /* PATHX_ETYPE */ "undefined variable", /* PATHX_ENOVAR */ "garbage at end of path expression", /* PATHX_EEND */ "no match for path expression", /* PATHX_ENOMATCH */ "wrong number of arguments in function call", /* PATHX_EARITY */ "invalid regular expression", /* PATHX_EREGEXP */ "too many matches", /* PATHX_EMMATCH */ "wrong flag for regexp" /* PATHX_EREGEXPFLAG */ }; /* * Path expressions are strings that use a notation modelled on XPath. */ enum type { T_NONE = 0, /* Not a type */ T_NODESET, T_BOOLEAN, T_NUMBER, T_STRING, T_REGEXP }; enum expr_tag { E_FILTER, E_BINARY, E_VALUE, E_VAR, E_APP }; enum binary_op { OP_EQ, /* '=' */ OP_NEQ, /* '!=' */ OP_LT, /* '<' */ OP_LE, /* '<=' */ OP_GT, /* '>' */ OP_GE, /* '>=' */ OP_PLUS, /* '+' */ OP_MINUS, /* '-' */ OP_STAR, /* '*' */ OP_AND, /* 'and' */ OP_OR, /* 'or' */ OP_ELSE, /* 'else' */ OP_RE_MATCH, /* '=~' */ OP_RE_NOMATCH, /* '!~' */ OP_UNION /* '|' */ }; struct pred { int nexpr; struct expr **exprs; }; enum axis { SELF, CHILD, SEQ, DESCENDANT, DESCENDANT_OR_SELF, PARENT, ANCESTOR, ROOT, PRECEDING_SIBLING, FOLLOWING_SIBLING }; /* This array is indexed by enum axis */ static const char *const axis_names[] = { "self", "child", "seq", /* Like child, but only selects node-names which are integers */ "descendant", "descendant-or-self", "parent", "ancestor", "root", "preceding-sibling", "following-sibling" }; /* The characters that can follow a name in a location expression (aka path) * The parser will assume that name (path component) is finished when it * encounters any of these characters, unless they are escaped by preceding * them with a '\\'. * * See parse_name for the gory details */ static const char name_follow[] = "][|/=()!,"; /* Doubly linked list of location steps. Besides the information from the * path expression, also contains information to iterate over a node set, * in particular, the context node CTX for the step, and the current node * CUR within that context. */ struct step { struct step *next; enum axis axis; char *name; /* NULL to match any name */ struct pred *predicates; }; /* Initialise the root nodeset with the first step */ static struct tree *step_root(struct step *step, struct tree *ctx, struct tree *root_ctx); /* Iteration over the nodes on a step, ignoring the predicates */ static struct tree *step_first(struct step *step, struct tree *ctx); static struct tree *step_next(struct step *step, struct tree *ctx, struct tree *node); struct pathx_symtab { struct pathx_symtab *next; char *name; struct value *value; }; struct pathx { struct state *state; struct nodeset *nodeset; int node; struct tree *origin; }; #define L_BRACK '[' #define R_BRACK ']' struct locpath { struct step *steps; }; struct nodeset { struct tree **nodes; size_t used; size_t size; }; typedef uint32_t value_ind_t; struct value { enum type tag; union { struct nodeset *nodeset; /* T_NODESET */ int64_t number; /* T_NUMBER */ char *string; /* T_STRING */ bool boolval; /* T_BOOLEAN */ struct regexp *regexp; /* T_REGEXP */ }; }; struct expr { enum expr_tag tag; enum type type; union { struct { /* E_FILTER */ struct expr *primary; struct pred *predicates; struct locpath *locpath; }; struct { /* E_BINARY */ enum binary_op op; struct expr *left; struct expr *right; bool left_matched; }; value_ind_t value_ind; /* E_VALUE */ char *ident; /* E_VAR */ struct { /* E_APP */ const struct func *func; struct expr **args; /* If fold is true, replace this function invocation * with its value after the first time we evaluate this * expression */ bool fold; }; }; }; struct locpath_trace { unsigned int maxns; struct nodeset **ns; struct locpath *lp; }; /* Internal state of the evaluator/parser */ struct state { pathx_errcode_t errcode; const char *file; int line; char *errmsg; const char *txt; /* Entire expression */ const char *pos; /* Current position within TXT during parsing */ struct tree *ctx; /* The current node */ uint ctx_pos; uint ctx_len; struct tree *root_ctx; /* Root context for relative paths */ /* A table of all values. The table is dynamically reallocated, i.e. * pointers to struct value should not be used across calls that * might allocate new values * * value_pool[0] is always the boolean false, and value_pool[1] * always the boolean true */ struct value *value_pool; value_ind_t value_pool_used; value_ind_t value_pool_size; /* Stack of values (as indices into value_pool), with bottom of stack in values[0] */ value_ind_t *values; size_t values_used; size_t values_size; /* Stack of expressions, with bottom of stack in exprs[0] */ struct expr **exprs; size_t exprs_used; size_t exprs_size; /* Trace of a locpath evaluation, needed by pathx_expand_tree. Generally NULL, unless a trace is needed. */ struct locpath_trace *locpath_trace; /* Symbol table for variable lookups */ struct pathx_symtab *symtab; /* Error structure, used to communicate errors to struct augeas; * we never own this structure, and therefore never free it */ struct error *error; /* If a filter-expression contains the 'else' operator, we need * we need to evaluate the filter twice. The has_else flag * means we don't do this unless we really need to */ bool has_else; }; /* We consider NULL and the empty string to be equal */ ATTRIBUTE_PURE static inline int streqx(const char *s1, const char *s2) { if (s1 == NULL) return (s2 == NULL || strlen(s2) == 0); if (s2 == NULL) return strlen(s1) == 0; return STREQ(s1, s2); } /* Functions */ typedef void (*func_impl_t)(struct state *state, int nargs); struct func { const char *name; unsigned int arity; enum type type; bool pure; /* Result only depends on args */ const enum type *arg_types; func_impl_t impl; }; static void func_last(struct state *state, int nargs); static void func_position(struct state *state, int nargs); static void func_count(struct state *state, int nargs); static void func_label(struct state *state, int nargs); static void func_regexp(struct state *state, int nargs); static void func_regexp_flag(struct state *state, int nargs); static void func_glob(struct state *state, int nargs); static void func_int(struct state *state, int nargs); static void func_not(struct state *state, int nargs); static void func_modified(struct state *state, int nargs); static const enum type arg_types_nodeset[] = { T_NODESET }; static const enum type arg_types_string[] = { T_STRING }; static const enum type arg_types_bool[] = { T_BOOLEAN }; static const enum type arg_types_string_string[] = { T_STRING, T_STRING }; static const enum type arg_types_nodeset_string[] = { T_NODESET, T_STRING }; static const struct func builtin_funcs[] = { { .name = "last", .arity = 0, .type = T_NUMBER, .arg_types = NULL, .impl = func_last, .pure = false }, { .name = "position", .arity = 0, .type = T_NUMBER, .arg_types = NULL, .impl = func_position, .pure = false }, { .name = "label", .arity = 0, .type = T_STRING, .arg_types = NULL, .impl = func_label, .pure = false }, { .name = "count", .arity = 1, .type = T_NUMBER, .arg_types = arg_types_nodeset, .impl = func_count, .pure = false }, { .name = "regexp", .arity = 1, .type = T_REGEXP, .arg_types = arg_types_string, .impl = func_regexp, .pure = true }, { .name = "regexp", .arity = 1, .type = T_REGEXP, .arg_types = arg_types_nodeset, .impl = func_regexp, .pure = true }, { .name = "regexp", .arity = 2, .type = T_REGEXP, .arg_types = arg_types_string_string, .impl = func_regexp_flag, .pure = true }, { .name = "regexp", .arity = 2, .type = T_REGEXP, .arg_types = arg_types_nodeset_string, .impl = func_regexp_flag, .pure = true }, { .name = "glob", .arity = 1, .type = T_REGEXP, .arg_types = arg_types_string, .impl = func_glob, .pure = true }, { .name = "glob", .arity = 1, .type = T_REGEXP, .arg_types = arg_types_nodeset, .impl = func_glob, .pure = true }, { .name = "int", .arity = 1, .type = T_NUMBER, .arg_types = arg_types_string, .impl = func_int, .pure = false }, { .name = "int", .arity = 1, .type = T_NUMBER, .arg_types = arg_types_nodeset, .impl = func_int, .pure = false }, { .name = "int", .arity = 1, .type = T_NUMBER, .arg_types = arg_types_bool, .impl = func_int, .pure = false }, { .name = "modified", .arity = 0, .type = T_BOOLEAN, .arg_types = NULL, .impl = func_modified, .pure = false }, { .name = "not", .arity = 1, .type = T_BOOLEAN, .arg_types = arg_types_bool, .impl = func_not, .pure = true } }; #define RET_ON_ERROR \ if (state->errcode != PATHX_NOERROR) return #define RET0_ON_ERROR \ if (state->errcode != PATHX_NOERROR) return 0 #define STATE_ERROR(state, err) \ do { \ state->errcode = err; \ state->file = __FILE__; \ state->line = __LINE__; \ } while (0) #define HAS_ERROR(state) (state->errcode != PATHX_NOERROR) #define STATE_ENOMEM STATE_ERROR(state, PATHX_ENOMEM) /* * Free the various data structures */ static void free_expr(struct expr *expr); static void free_pred(struct pred *pred) { if (pred == NULL) return; for (int i=0; i < pred->nexpr; i++) { free_expr(pred->exprs[i]); } free(pred->exprs); free(pred); } static void free_step(struct step *step) { while (step != NULL) { struct step *del = step; step = del->next; free(del->name); free_pred(del->predicates); free(del); } } static void free_locpath(struct locpath *locpath) { if (locpath == NULL) return; while (locpath->steps != NULL) { struct step *step = locpath->steps; locpath->steps = step->next; free(step->name); free_pred(step->predicates); free(step); } free(locpath); } static void free_expr(struct expr *expr) { if (expr == NULL) return; switch (expr->tag) { case E_FILTER: free_expr(expr->primary); free_pred(expr->predicates); free_locpath(expr->locpath); break; case E_BINARY: free_expr(expr->left); free_expr(expr->right); break; case E_VALUE: break; case E_VAR: free(expr->ident); break; case E_APP: for (int i=0; i < expr->func->arity; i++) free_expr(expr->args[i]); free(expr->args); break; default: assert(0); } free(expr); } static void free_nodeset(struct nodeset *ns) { if (ns != NULL) { free(ns->nodes); free(ns); } } /* Free all objects used by VALUE, but not VALUE itself */ static void release_value(struct value *v) { if (v == NULL) return; switch (v->tag) { case T_NODESET: free_nodeset(v->nodeset); break; case T_STRING: free(v->string); break; case T_BOOLEAN: case T_NUMBER: break; case T_REGEXP: unref(v->regexp, regexp); break; default: assert(0); } } static void free_state(struct state *state) { if (state == NULL) return; for(int i=0; i < state->exprs_used; i++) free_expr(state->exprs[i]); free(state->exprs); for(int i=0; i < state->value_pool_used; i++) release_value(state->value_pool + i); free(state->value_pool); free(state->values); free(state); } void free_pathx(struct pathx *pathx) { if (pathx == NULL) return; free_state(pathx->state); free(pathx); } /* * Nodeset helpers */ static struct nodeset *make_nodeset(struct state *state) { struct nodeset *result; if (ALLOC(result) < 0) STATE_ENOMEM; return result; } /* Add NODE to NS if it is not in NS yet. This relies on the flag * NODE->ADDED and care must be taken that NS_CLEAR_ADDED is called on NS * as soon as we are done adding nodes to it. */ static void ns_add(struct nodeset *ns, struct tree *node, struct state *state) { if (node->added) return; if (ns->used >= ns->size) { size_t size = 2 * ns->size; if (size < 10) size = 10; if (REALLOC_N(ns->nodes, size) < 0) STATE_ENOMEM; ns->size = size; } ns->nodes[ns->used] = node; node->added = 1; ns->used += 1; } static void ns_clear_added(struct nodeset *ns) { for (int i=0; i < ns->used; i++) ns->nodes[i]->added = 0; } static struct nodeset * clone_nodeset(struct nodeset *ns, struct state *state) { struct nodeset *clone; if (ALLOC(clone) < 0) { STATE_ENOMEM; return NULL; } if (ALLOC_N(clone->nodes, ns->used) < 0) { free(clone); STATE_ENOMEM; return NULL; } clone->used = ns->used; clone->size = ns->used; for (int i=0; i < ns->used; i++) clone->nodes[i] = ns->nodes[i]; return clone; } /* * Handling values */ static value_ind_t make_value(enum type tag, struct state *state) { assert(tag != T_BOOLEAN); if (state->value_pool_used >= state->value_pool_size) { value_ind_t new_size = 2*state->value_pool_size; if (new_size <= state->value_pool_size) { STATE_ENOMEM; return 0; } if (REALLOC_N(state->value_pool, new_size) < 0) { STATE_ENOMEM; return 0; } state->value_pool_size = new_size; } state->value_pool[state->value_pool_used].tag = tag; state->value_pool[state->value_pool_used].nodeset = NULL; return state->value_pool_used++; } static value_ind_t clone_value(struct value *v, struct state *state) { value_ind_t vind = make_value(v->tag, state); RET0_ON_ERROR; struct value *clone = state->value_pool + vind; switch (v->tag) { case T_NODESET: clone->nodeset = clone_nodeset(v->nodeset, state); break; case T_NUMBER: clone->number = v->number; break; case T_STRING: clone->string = strdup(v->string); if (clone->string == NULL) { STATE_ENOMEM; } break; case T_BOOLEAN: clone->boolval = v->boolval; break; case T_REGEXP: clone->regexp = ref(v->regexp); break; default: assert(0); } return vind; } static value_ind_t pop_value_ind(struct state *state) { if (state->values_used > 0) { state->values_used -= 1; return state->values[state->values_used]; } else { STATE_ERROR(state, PATHX_EINTERNAL); assert(0); return 0; } } static struct value *pop_value(struct state *state) { value_ind_t vind = pop_value_ind(state); if (HAS_ERROR(state)) return NULL; return state->value_pool + vind; } static void push_value(value_ind_t vind, struct state *state) { if (state->values_used >= state->values_size) { size_t new_size = 2*state->values_size; if (new_size == 0) new_size = 8; if (REALLOC_N(state->values, new_size) < 0) { STATE_ENOMEM; return; } state->values_size = new_size; } state->values[state->values_used++] = vind; } static void push_boolean_value(int b, struct state *state) { push_value(b != 0, state); } ATTRIBUTE_PURE static struct value *expr_value(struct expr *expr, struct state *state) { return state->value_pool + expr->value_ind; } /************************************************************************* * Evaluation ************************************************************************/ static void eval_expr(struct expr *expr, struct state *state); #define ensure_arity(min, max) \ if (nargs < min || nargs > max) { \ STATE_ERROR(state, PATHX_EINTERNAL); \ return; \ } static void func_last(struct state *state, int nargs) { ensure_arity(0, 0); value_ind_t vind = make_value(T_NUMBER, state); RET_ON_ERROR; state->value_pool[vind].number = state->ctx_len; push_value(vind, state); } static void func_position(struct state *state, int nargs) { ensure_arity(0, 0); value_ind_t vind = make_value(T_NUMBER, state); RET_ON_ERROR; state->value_pool[vind].number = state->ctx_pos; push_value(vind, state); } static void func_count(struct state *state, int nargs) { ensure_arity(1, 1); value_ind_t vind = make_value(T_NUMBER, state); RET_ON_ERROR; struct value *ns = pop_value(state); state->value_pool[vind].number = ns->nodeset->used; push_value(vind, state); } static void func_label(struct state *state, int nargs) { ensure_arity(0, 0); value_ind_t vind = make_value(T_STRING, state); char *s; RET_ON_ERROR; if (state->ctx->label) s = strdup(state->ctx->label); else s = strdup(""); if (s == NULL) { STATE_ENOMEM; return; } state->value_pool[vind].string = s; push_value(vind, state); } static void func_int(struct state *state, int nargs) { ensure_arity(1, 1); value_ind_t vind = make_value(T_NUMBER, state); int64_t i = -1; RET_ON_ERROR; struct value *v = pop_value(state); if (v->tag == T_BOOLEAN) { i = v->boolval; } else { const char *s = NULL; if (v->tag == T_STRING) { s = v->string; } else { /* T_NODESET */ if (v->nodeset->used != 1) { STATE_ERROR(state, PATHX_EMMATCH); return; } s = v->nodeset->nodes[0]->value; } if (s != NULL) { int r; r = xstrtoint64(s, 10, &i); if (r < 0) { STATE_ERROR(state, PATHX_ENUMBER); return; } } } state->value_pool[vind].number = i; push_value(vind, state); } static void func_modified(struct state *state, int nargs) { ensure_arity(0, 0); push_boolean_value(state->ctx->dirty , state); } static void func_not(struct state *state, int nargs) { ensure_arity(1, 1); RET_ON_ERROR; struct value *v = pop_value(state); if (v->tag == T_BOOLEAN) { push_boolean_value(! v->boolval, state); } } static struct regexp * nodeset_as_regexp(struct info *info, struct nodeset *ns, int glob, int nocase) { struct regexp *result = NULL; struct regexp **rx = NULL; int used = 0; for (int i = 0; i < ns->used; i++) { if (ns->nodes[i]->value != NULL) used += 1; } if (used == 0) { /* If the nodeset is empty, make sure we produce a regexp * that never matches anything */ result = make_regexp_unescape(info, "[^\001-\7ff]", nocase); } else { if (ALLOC_N(rx, ns->used) < 0) goto error; for (int i=0; i < ns->used; i++) { if (ns->nodes[i]->value == NULL) continue; if (glob) rx[i] = make_regexp_from_glob(info, ns->nodes[i]->value); else rx[i] = make_regexp_unescape(info, ns->nodes[i]->value, 0); if (rx[i] == NULL) goto error; } result = regexp_union_n(info, ns->used, rx); } error: if (rx != NULL) { for (int i=0; i < ns->used; i++) unref(rx[i], regexp); free(rx); } return result; } static void func_regexp_or_glob(struct state *state, int glob, int nocase) { value_ind_t vind = make_value(T_REGEXP, state); int r; RET_ON_ERROR; struct value *v = pop_value(state); struct regexp *rx = NULL; if (v->tag == T_STRING) { if (glob) rx = make_regexp_from_glob(state->error->info, v->string); else rx = make_regexp_unescape(state->error->info, v->string, nocase); } else if (v->tag == T_NODESET) { rx = nodeset_as_regexp(state->error->info, v->nodeset, glob, nocase); } else { assert(0); } if (rx == NULL) { STATE_ENOMEM; return; } state->value_pool[vind].regexp = rx; r = regexp_compile(rx); if (r < 0) { const char *msg; regexp_check(rx, &msg); state->errmsg = strdup(msg); STATE_ERROR(state, PATHX_EREGEXP); return; } push_value(vind, state); } static void func_regexp(struct state *state, int nargs) { ensure_arity(1, 1); func_regexp_or_glob(state, 0, 0); } static void func_regexp_flag(struct state *state, int nargs) { ensure_arity(2, 2); int nocase = 0; struct value *f = pop_value(state); if (STREQ("i", f->string)) nocase = 1; else STATE_ERROR(state, PATHX_EREGEXPFLAG); func_regexp_or_glob(state, 0, nocase); } static void func_glob(struct state *state, int nargs) { ensure_arity(1, 1); func_regexp_or_glob(state, 1, 0); } static bool coerce_to_bool(struct value *v) { switch (v->tag) { case T_NODESET: return v->nodeset->used > 0; break; case T_BOOLEAN: return v->boolval; break; case T_NUMBER: return v->number > 0; break; case T_STRING: return strlen(v->string) > 0; break; case T_REGEXP: return true; default: assert(0); return false; } } static int calc_eq_nodeset_nodeset(struct nodeset *ns1, struct nodeset *ns2, int neq) { for (int i1=0; i1 < ns1->used; i1++) { struct tree *t1 = ns1->nodes[i1]; for (int i2=0; i2 < ns2->used; i2++) { struct tree *t2 = ns2->nodes[i2]; if (neq) { if (!streqx(t1->value, t2->value)) return 1; } else { if (streqx(t1->value, t2->value)) return 1; } } } return 0; } static int calc_eq_nodeset_string(struct nodeset *ns, const char *s, int neq) { for (int i=0; i < ns->used; i++) { struct tree *t = ns->nodes[i]; if (neq) { if (!streqx(t->value, s)) return 1; } else { if (streqx(t->value, s)) return 1; } } return 0; } static void eval_eq(struct state *state, int neq) { struct value *r = pop_value(state); struct value *l = pop_value(state); int res; if (l->tag == T_NODESET && r->tag == T_NODESET) { res = calc_eq_nodeset_nodeset(l->nodeset, r->nodeset, neq); } else if (l->tag == T_NODESET) { res = calc_eq_nodeset_string(l->nodeset, r->string, neq); } else if (r->tag == T_NODESET) { res = calc_eq_nodeset_string(r->nodeset, l->string, neq); } else if (l->tag == T_NUMBER && r->tag == T_NUMBER) { if (neq) res = (l->number != r->number); else res = (l->number == r->number); } else { assert(l->tag == T_STRING); assert(r->tag == T_STRING); res = streqx(l->string, r->string); if (neq) res = !res; } RET_ON_ERROR; push_boolean_value(res, state); } static void eval_arith(struct state *state, enum binary_op op) { value_ind_t vind = make_value(T_NUMBER, state); struct value *r = pop_value(state); struct value *l = pop_value(state); int res; assert(l->tag == T_NUMBER); assert(r->tag == T_NUMBER); RET_ON_ERROR; if (op == OP_PLUS) res = l->number + r->number; else if (op == OP_MINUS) res = l->number - r->number; else if (op == OP_STAR) res = l->number * r->number; else assert(0); state->value_pool[vind].number = res; push_value(vind, state); } static void eval_rel(struct state *state, bool greater, bool equal) { struct value *r, *l; int res; /* We always check l < r or l <= r */ if (greater) { l = pop_value(state); r = pop_value(state); } else { r = pop_value(state); l = pop_value(state); } if (l->tag == T_NUMBER) { if (equal) res = (l->number <= r->number); else res = (l->number < r->number); } else if (l->tag == T_STRING) { int cmp = strcmp(l->string, r->string); if (equal) res = cmp <= 0; else res = cmp < 0; } else { assert(0); } push_boolean_value(res, state); } static void eval_and_or(struct state *state, enum binary_op op) { struct value *r = pop_value(state); struct value *l = pop_value(state); bool left = coerce_to_bool(l); bool right = coerce_to_bool(r); if (op == OP_AND) push_boolean_value(left && right, state); else push_boolean_value(left || right, state); } static void eval_else(struct state *state, struct expr *expr, struct locpath_trace *lpt_right) { struct value *r = pop_value(state); struct value *l = pop_value(state); if ( l->tag == T_NODESET && r->tag == T_NODESET ) { int discard_maxns=0; struct nodeset **discard_ns=NULL; struct locpath_trace *lpt = state->locpath_trace; value_ind_t vind = make_value(T_NODESET, state); if (l->nodeset->used >0 || expr->left_matched) { expr->left_matched = 1; state->value_pool[vind].nodeset = clone_nodeset(l->nodeset, state); if( lpt_right != NULL ) { discard_maxns = lpt_right->maxns; discard_ns = lpt_right->ns; } } else { state->value_pool[vind].nodeset = clone_nodeset(r->nodeset, state); if( lpt != NULL && lpt_right != NULL ) { discard_maxns = lpt->maxns; discard_ns = lpt->ns; lpt->maxns = lpt_right->maxns; lpt->ns = lpt_right->ns; lpt->lp = lpt_right->lp; } } push_value(vind, state); if ( lpt != NULL && lpt_right != NULL ) { for (int i=0; i < discard_maxns; i++) free_nodeset(discard_ns[i]); FREE(discard_ns); } } else { bool left = coerce_to_bool(l); bool right = coerce_to_bool(r); expr->left_matched = expr->left_matched || left; if (expr->left_matched) { /* One or more LHS have matched, so we're not interested in the right expr */ push_boolean_value(left, state); } else { /* no LHS has matched (yet), so keep the right expr */ /* If this is the 2nd pass, and expr->left_matched is true, no RHS nodes will be included */ push_boolean_value(right, state); } } } static bool eval_re_match_str(struct state *state, struct regexp *rx, const char *str) { int r; if (str == NULL) str = ""; r = regexp_match(rx, str, strlen(str), 0, NULL); if (r == -2) { STATE_ERROR(state, PATHX_EINTERNAL); } else if (r == -3) { /* We should never get this far; func_regexp should catch * invalid regexps */ assert(false); } return r == strlen(str); } static void eval_union(struct state *state, struct locpath_trace *lpt_right) { value_ind_t vind = make_value(T_NODESET, state); struct value *r = pop_value(state); struct value *l = pop_value(state); struct nodeset *res = NULL; struct locpath_trace *lpt = state->locpath_trace; assert(l->tag == T_NODESET); assert(r->tag == T_NODESET); RET_ON_ERROR; res = clone_nodeset(l->nodeset, state); RET_ON_ERROR; for (int i=0; i < r->nodeset->used; i++) { ns_add(res, r->nodeset->nodes[i], state); if (HAS_ERROR(state)) goto error; } state->value_pool[vind].nodeset = res; push_value(vind, state); if( lpt != NULL && lpt_right != NULL ) { STATE_ERROR(state, PATHX_EMMATCH); for (int i=0; i < lpt_right->maxns; i++) free_nodeset(lpt_right->ns[i]); FREE(lpt_right->ns); } error: ns_clear_added(res); } static void eval_concat_string(struct state *state) { value_ind_t vind = make_value(T_STRING, state); struct value *r = pop_value(state); struct value *l = pop_value(state); char *res = NULL; RET_ON_ERROR; if (ALLOC_N(res, strlen(l->string) + strlen(r->string) + 1) < 0) { STATE_ENOMEM; return; } strcpy(res, l->string); strcat(res, r->string); state->value_pool[vind].string = res; push_value(vind, state); } static void eval_concat_regexp(struct state *state) { value_ind_t vind = make_value(T_REGEXP, state); struct value *r = pop_value(state); struct value *l = pop_value(state); struct regexp *rx = NULL; RET_ON_ERROR; rx = regexp_concat(state->error->info, l->regexp, r->regexp); if (rx == NULL) { STATE_ENOMEM; return; } state->value_pool[vind].regexp = rx; push_value(vind, state); } static void eval_re_match(struct state *state, enum binary_op op) { struct value *rx = pop_value(state); struct value *v = pop_value(state); bool result = false; if (v->tag == T_STRING) { result = eval_re_match_str(state, rx->regexp, v->string); RET_ON_ERROR; } else if (v->tag == T_NODESET) { for (int i=0; i < v->nodeset->used && result == false; i++) { struct tree *t = v->nodeset->nodes[i]; result = eval_re_match_str(state, rx->regexp, t->value); RET_ON_ERROR; } } if (op == OP_RE_NOMATCH) result = !result; push_boolean_value(result, state); } static void eval_binary(struct expr *expr, struct state *state) { struct locpath_trace *lpt = state->locpath_trace; struct locpath_trace lpt_right; eval_expr(expr->left, state); if ( lpt != NULL && expr->type == T_NODESET ) { MEMZERO(&lpt_right, 1); state->locpath_trace = &lpt_right; } eval_expr(expr->right, state); state->locpath_trace = lpt; RET_ON_ERROR; switch (expr->op) { case OP_EQ: eval_eq(state, 0); break; case OP_NEQ: eval_eq(state, 1); break; case OP_LT: eval_rel(state, false, false); break; case OP_LE: eval_rel(state, false, true); break; case OP_GT: eval_rel(state, true, false); break; case OP_GE: eval_rel(state, true, true); break; case OP_PLUS: if (expr->type == T_NUMBER) eval_arith(state, expr->op); else if (expr->type == T_STRING) eval_concat_string(state); else if (expr->type == T_REGEXP) eval_concat_regexp(state); break; case OP_MINUS: case OP_STAR: eval_arith(state, expr->op); break; case OP_AND: case OP_OR: eval_and_or(state, expr->op); break; case OP_ELSE: eval_else(state, expr, &lpt_right); break; case OP_UNION: eval_union(state, &lpt_right); break; case OP_RE_MATCH: case OP_RE_NOMATCH: eval_re_match(state, expr->op); break; default: assert(0); } } static void eval_app(struct expr *expr, struct state *state) { assert(expr->tag == E_APP); for (int i=0; i < expr->func->arity; i++) { eval_expr(expr->args[i], state); RET_ON_ERROR; } expr->func->impl(state, expr->func->arity); } static bool eval_pred(struct expr *expr, struct state *state) { eval_expr(expr, state); RET0_ON_ERROR; struct value *v = pop_value(state); switch(v->tag) { case T_BOOLEAN: return v->boolval; case T_NUMBER: return (state->ctx_pos == v->number); case T_NODESET: return v->nodeset->used > 0; case T_STRING: return streqv(state->ctx->value, v->string); default: assert(0); return false; } } /* Remove COUNT successive entries from NS. The first entry to remove is at IND */ static void ns_remove(struct nodeset *ns, int ind, int count) { if (count < 1) return; memmove(ns->nodes + ind, ns->nodes + ind+count, sizeof(ns->nodes[0]) * (ns->used - (ind+count))); ns->used -= count; } /* * Remove all nodes from NS for which one of PRED is false */ static void ns_filter(struct nodeset *ns, struct pred *predicates, struct state *state) { if (predicates == NULL) return; struct tree *old_ctx = state->ctx; uint old_ctx_len = state->ctx_len; uint old_ctx_pos = state->ctx_pos; for (int p=0; p < predicates->nexpr; p++) { if ( state->has_else) { for (int i=0; i < ns->used; i++) { /* 1st pass, check if any else statements have match on the left */ /* Don't delete any nodes (yet) */ state->ctx = ns->nodes[i]; eval_pred(predicates->exprs[p], state); } } int first_bad = -1; /* The index of the first non-matching node */ state->ctx_len = ns->used; state->ctx_pos = 1; for (int i=0; i < ns->used; state->ctx_pos++) { state->ctx = ns->nodes[i]; bool match = eval_pred(predicates->exprs[p], state); RET_ON_ERROR; /* We remove non-matching nodes from NS in batches; this logic * makes sure that we only call ns_remove at the end of a run * of non-matching nodes */ if (match) { if (first_bad >= 0) { ns_remove(ns, first_bad, i - first_bad); i = first_bad + 1; } else { i += 1; } first_bad = -1; } else { if (first_bad == -1) first_bad = i; i += 1; } } if (first_bad >= 0) { ns_remove(ns, first_bad, ns->used - first_bad); } } state->ctx = old_ctx; state->ctx_pos = old_ctx_pos; state->ctx_len = old_ctx_len; } /* Return true if PRED is solely the predicate '[n]' as in 'foo[17]' */ static bool position_pred(struct pred *pred) { return pred != NULL && pred->nexpr == 1 && pred->exprs[0]->tag == E_VALUE && pred->exprs[0]->type == T_NUMBER; } /* Return the tree node at the position implied by STEP->PREDICATES. It is assumed and required that STEP->PREDICATES is actually a POSITION_PRED. This method hand-optimizes the important case of a path expression like 'service[42]' */ static struct tree *position_filter(struct nodeset *ns, struct step *step, struct state *state) { int value_ind = step->predicates->exprs[0]->value_ind; int number = state->value_pool[value_ind].number; int pos = 1; for (int i=0; i < ns->used; i++) { for (struct tree *node = step_first(step, ns->nodes[i]); node != NULL; node = step_next(step, ns->nodes[i], node), pos++) { if (pos == number) return node; } } return NULL; } /* Return an array of nodesets, one for each step in the locpath. * * On return, (*NS)[0] will contain state->ctx, and (*NS)[*MAXNS] will * contain the nodes that matched the entire locpath */ static void ns_from_locpath(struct locpath *lp, uint *maxns, struct nodeset ***ns, const struct nodeset *root, struct state *state) { struct tree *old_ctx = state->ctx; *maxns = 0; ensure(lp != NULL, state); *ns = NULL; list_for_each(step, lp->steps) *maxns += 1; if (ALLOC_N(*ns, *maxns+1) < 0) { STATE_ERROR(state, PATHX_ENOMEM); goto error; } for (int i=0; i <= *maxns; i++) { (*ns)[i] = make_nodeset(state); if (HAS_ERROR(state)) goto error; } if (root == NULL) { struct step *first_step = NULL; first_step = lp->steps; struct tree *root_tree; root_tree = step_root(first_step, state->ctx, state->root_ctx); ns_add((*ns)[0], root_tree, state); ns_clear_added((*ns)[0]); } else { for (int i=0; i < root->used; i++) ns_add((*ns)[0], root->nodes[i], state); ns_clear_added((*ns)[0]); } if (HAS_ERROR(state)) goto error; uint cur_ns = 0; list_for_each(step, lp->steps) { struct nodeset *work = (*ns)[cur_ns]; struct nodeset *next = (*ns)[cur_ns + 1]; if (position_pred(step->predicates)) { struct tree *node = position_filter(work, step, state); if (node) { ns_add(next, node, state); ns_clear_added(next); } } else { for (int i=0; i < work->used; i++) { for (struct tree *node = step_first(step, work->nodes[i]); node != NULL; node = step_next(step, work->nodes[i], node)) { ns_add(next, node, state); } } ns_clear_added(next); ns_filter(next, step->predicates, state); if (HAS_ERROR(state)) goto error; } cur_ns += 1; } state->ctx = old_ctx; return; error: if (*ns != NULL) { for (int i=0; i <= *maxns; i++) free_nodeset((*ns)[i]); FREE(*ns); } state->ctx = old_ctx; return; } static void eval_filter(struct expr *expr, struct state *state) { struct locpath *lp = expr->locpath; struct nodeset **ns = NULL; struct locpath_trace *lpt = state->locpath_trace; uint maxns; state->locpath_trace = NULL; if (expr->primary == NULL) { ns_from_locpath(lp, &maxns, &ns, NULL, state); } else { eval_expr(expr->primary, state); RET_ON_ERROR; value_ind_t primary_ind = pop_value_ind(state); struct value *primary = state->value_pool + primary_ind; assert(primary->tag == T_NODESET); ns_filter(primary->nodeset, expr->predicates, state); /* Evaluating predicates might have reallocated the value_pool */ primary = state->value_pool + primary_ind; ns_from_locpath(lp, &maxns, &ns, primary->nodeset, state); } RET_ON_ERROR; value_ind_t vind = make_value(T_NODESET, state); RET_ON_ERROR; state->value_pool[vind].nodeset = ns[maxns]; push_value(vind, state); if (lpt != NULL) { assert(lpt->ns == NULL); assert(lpt->lp == NULL); lpt->maxns = maxns; lpt->ns = ns; lpt->lp = lp; state->locpath_trace = lpt; } else { for (int i=0; i < maxns; i++) free_nodeset(ns[i]); FREE(ns); } } static struct value *lookup_var(const char *ident, const struct pathx_symtab *symtab) { list_for_each(tab, symtab) { if (STREQ(ident, tab->name)) return tab->value; } return NULL; } static void eval_var(struct expr *expr, struct state *state) { struct value *v = lookup_var(expr->ident, state->symtab); value_ind_t vind = clone_value(v, state); RET_ON_ERROR; push_value(vind, state); } static void eval_expr(struct expr *expr, struct state *state) { RET_ON_ERROR; switch (expr->tag) { case E_FILTER: eval_filter(expr, state); break; case E_BINARY: eval_binary(expr, state); break; case E_VALUE: push_value(expr->value_ind, state); break; case E_VAR: eval_var(expr, state); break; case E_APP: eval_app(expr, state); if (expr->fold) { /* Do constant folding: replace the function application with * a reference to the value that resulted from evaluating it */ for (int i=0; i < expr->func->arity; i++) free_expr(expr->args[i]); free(expr->args); value_ind_t vind = state->values_used - 1; expr->tag = E_VALUE; expr->value_ind = state->values[vind]; } break; default: assert(0); } } /************************************************************************* * Typechecker *************************************************************************/ static void check_expr(struct expr *expr, struct state *state); /* Typecheck a list of predicates. A predicate is a function of * one of the following types: * * T_NODESET -> T_BOOLEAN * T_NUMBER -> T_BOOLEAN (position test) * T_BOOLEAN -> T_BOOLEAN */ static void check_preds(struct pred *pred, struct state *state) { if (pred == NULL) return; for (int i=0; i < pred->nexpr; i++) { struct expr *e = pred->exprs[i]; check_expr(e, state); RET_ON_ERROR; if (e->type != T_NODESET && e->type != T_NUMBER && e->type != T_BOOLEAN && e->type != T_STRING) { STATE_ERROR(state, PATHX_ETYPE); return; } } } static void check_filter(struct expr *expr, struct state *state) { assert(expr->tag == E_FILTER); struct locpath *locpath = expr->locpath; if (expr->primary != NULL) { check_expr(expr->primary, state); if (expr->primary->type != T_NODESET) { STATE_ERROR(state, PATHX_ETYPE); return; } check_preds(expr->predicates, state); RET_ON_ERROR; } list_for_each(s, locpath->steps) { check_preds(s->predicates, state); RET_ON_ERROR; } expr->type = T_NODESET; } static void check_app(struct expr *expr, struct state *state) { assert(expr->tag == E_APP); for (int i=0; i < expr->func->arity; i++) { check_expr(expr->args[i], state); RET_ON_ERROR; } int f; for (f=0; f < ARRAY_CARDINALITY(builtin_funcs); f++) { const struct func *fn = builtin_funcs + f; if (STRNEQ(expr->func->name, fn->name)) continue; if (expr->func->arity != fn->arity) continue; int match = 1; for (int i=0; i < expr->func->arity; i++) { if (expr->args[i]->type != fn->arg_types[i]) { match = 0; break; } } if (match) break; } if (f < ARRAY_CARDINALITY(builtin_funcs)) { expr->func = builtin_funcs + f; expr->type = expr->func->type; expr->fold = expr->func->pure; if (expr->fold) { /* We only do constant folding for invocations of pure functions * whose arguments are literal values. That misses opportunities * for constant folding, e.g., "regexp('foo' + 'bar')" but is * a bit simpler than doing full tracking of constants */ for (int i=0; i < expr->func->arity; i++) { if (expr->args[i]->tag != E_VALUE) expr->fold = false; } } } else { STATE_ERROR(state, PATHX_ETYPE); } } /* Check the binary operators. Type rules: * * '=', '!=' : T_NODESET -> T_NODESET -> T_BOOLEAN * T_STRING -> T_NODESET -> T_BOOLEAN * T_NODESET -> T_STRING -> T_BOOLEAN * T_NUMBER -> T_NUMBER -> T_BOOLEAN * * '>', '>=', * '<', '<=' : T_NUMBER -> T_NUMBER -> T_BOOLEAN * T_STRING -> T_STRING -> T_BOOLEAN * '+' : T_NUMBER -> T_NUMBER -> T_NUMBER * T_STRING -> T_STRING -> T_STRING * T_REGEXP -> T_REGEXP -> T_REGEXP * '+', '-', '*': T_NUMBER -> T_NUMBER -> T_NUMBER * * 'and', 'or': T_BOOLEAN -> T_BOOLEAN -> T_BOOLEAN * '=~', '!~' : T_STRING -> T_REGEXP -> T_BOOLEAN * T_NODESET -> T_REGEXP -> T_BOOLEAN * * '|' : T_NODESET -> T_NODESET -> T_NODESET * * Any type can be coerced to T_BOOLEAN (see coerce_to_bool) */ static void check_binary(struct expr *expr, struct state *state) { check_expr(expr->left, state); check_expr(expr->right, state); RET_ON_ERROR; enum type l = expr->left->type; enum type r = expr->right->type; int ok = 1; enum type res; switch(expr->op) { case OP_EQ: case OP_NEQ: ok = ((l == T_NODESET || l == T_STRING) && (r == T_NODESET || r == T_STRING)) || (l == T_NUMBER && r == T_NUMBER);; res = T_BOOLEAN; break; case OP_LT: case OP_LE: case OP_GT: case OP_GE: ok = (l == T_NUMBER && r == T_NUMBER) || (l == T_STRING && r == T_STRING); res = T_BOOLEAN; break; case OP_PLUS: ok = (l == r && (l == T_NUMBER || l == T_STRING || l == T_REGEXP)); res = l; break; case OP_MINUS: case OP_STAR: ok = (l == T_NUMBER && r == T_NUMBER); res = T_NUMBER; break; case OP_UNION: ok = (l == T_NODESET && r == T_NODESET); res = T_NODESET; break; case OP_AND: case OP_OR: ok = 1; res = T_BOOLEAN; break; case OP_ELSE: if (l == T_NODESET && r == T_NODESET) { res = T_NODESET; } else { res = T_BOOLEAN; } ok = 1; break; case OP_RE_MATCH: case OP_RE_NOMATCH: ok = ((l == T_STRING || l == T_NODESET) && r == T_REGEXP); res = T_BOOLEAN; break; default: assert(0); } if (! ok) { STATE_ERROR(state, PATHX_ETYPE); } else { expr->type = res; } } static void check_var(struct expr *expr, struct state *state) { struct value *v = lookup_var(expr->ident, state->symtab); if (v == NULL) { STATE_ERROR(state, PATHX_ENOVAR); return; } expr->type = v->tag; } /* Typecheck an expression */ static void check_expr(struct expr *expr, struct state *state) { RET_ON_ERROR; switch(expr->tag) { case E_FILTER: check_filter(expr, state); break; case E_BINARY: check_binary(expr, state); break; case E_VALUE: expr->type = expr_value(expr, state)->tag; break; case E_VAR: check_var(expr, state); break; case E_APP: check_app(expr, state); break; default: assert(0); } } /* * Utility functions for the parser */ static void skipws(struct state *state) { while (isspace(*state->pos)) state->pos += 1; } static int match(struct state *state, char m) { skipws(state); if (*state->pos == '\0') return 0; if (*state->pos == m) { state->pos += 1; return 1; } return 0; } static int peek(struct state *state, const char *chars) { return strchr(chars, *state->pos) != NULL; } /* Return 1 if STATE->POS starts with TOKEN, followed by optional * whitespace, followed by FOLLOW. In that case, STATE->POS is set to the * first character after FOLLOW. Return 0 otherwise and leave STATE->POS * unchanged. */ static int looking_at(struct state *state, const char *token, const char *follow) { if (STREQLEN(state->pos, token, strlen(token))) { const char *p = state->pos + strlen(token); while (isspace(*p)) p++; if (STREQLEN(p, follow, strlen(follow))) { state->pos = p + strlen(follow); return 1; } } return 0; } /************************************************************************* * The parser *************************************************************************/ static void parse_expr(struct state *state); static struct expr* pop_expr(struct state *state) { if (state->exprs_used > 0) { state->exprs_used -= 1; return state->exprs[state->exprs_used]; } else { STATE_ERROR(state, PATHX_EINTERNAL); assert(0); return NULL; } } static void push_expr(struct expr *expr, struct state *state) { if (state->exprs_used >= state->exprs_size) { size_t new_size = 2*state->exprs_size; if (new_size == 0) new_size = 8; if (REALLOC_N(state->exprs, new_size) < 0) { STATE_ENOMEM; return; } state->exprs_size = new_size; } state->exprs[state->exprs_used++] = expr; } static void push_new_binary_op(enum binary_op op, struct state *state) { struct expr *expr = NULL; if (ALLOC(expr) < 0) { STATE_ENOMEM; return; } expr->tag = E_BINARY; expr->op = op; expr->right = pop_expr(state); expr->left = pop_expr(state); expr->left_matched = false; /* for 'else' operator only, true if any matches on LHS */ push_expr(expr, state); } int pathx_escape_name(const char *in, char **out) { const char *p; int num_to_escape = 0; char *s; *out = NULL; for (p = in; *p; p++) { if (strchr(name_follow, *p) || isspace(*p) || *p == '\\') num_to_escape += 1; } if (num_to_escape == 0) return 0; if (ALLOC_N(*out, strlen(in) + num_to_escape + 1) < 0) return -1; for (p = in, s = *out; *p; p++) { if (strchr(name_follow, *p) || isspace(*p) || *p == '\\') *s++ = '\\'; *s++ = *p; } *s = '\0'; return 0; } /* Return true if POS is preceded by an odd number of backslashes, i.e., if * POS is escaped. Stop the search when we get to START */ static bool backslash_escaped(const char *pos, const char *start) { bool result=false; while (pos-- > start && *pos == '\\') { result = !result; } return result; } /* * NameNoWS ::= [^][|/\= \t\n] | \\. * NameWS ::= [^][|/\=] | \\. * Name ::= NameNoWS NameWS* NameNoWS | NameNoWS */ static char *parse_name(struct state *state) { const char *s = state->pos; char *result; /* Advance state->pos until it points to the first character that is * not part of a name. */ while (*state->pos != '\0' && strchr(name_follow, *state->pos) == NULL) { /* Since we allow spaces in names, we need to avoid gobbling up * stuff that is in follow(Name), e.g. 'or' so that things like * [name1 or name2] still work. In other words, we'll parse 'x frob * y' as one name, but for 'x or y', we consider 'x' a name in its * own right. */ if (STREQLEN(state->pos, " or ", strlen(" or ")) || STREQLEN(state->pos, " and ", strlen(" and ")) || STREQLEN(state->pos, " else ", strlen(" else "))) break; if (*state->pos == '\\') { state->pos += 1; if (*state->pos == '\0') { STATE_ERROR(state, PATHX_ENAME); return NULL; } } state->pos += 1; } /* Strip trailing white space. Make sure we respect escaped whitespace * and don't strip it as in "x\\ " */ if (state->pos > s) { state->pos -= 1; while (isspace(*state->pos) && state->pos > s && !backslash_escaped(state->pos, s)) state->pos -= 1; state->pos += 1; } if (state->pos == s) { STATE_ERROR(state, PATHX_ENAME); return NULL; } result = strndup(s, state->pos - s); if (result == NULL) { STATE_ENOMEM; return NULL; } char *p = result; for (char *t = result; *t != '\0'; t++, p++) { if (*t == '\\') t += 1; *p = *t; } *p = '\0'; return result; } /* * Predicate ::= "[" Expr "]" * */ static struct pred *parse_predicates(struct state *state) { struct pred *pred = NULL; int nexpr = 0; while (match(state, L_BRACK)) { parse_expr(state); nexpr += 1; RET0_ON_ERROR; if (! match(state, R_BRACK)) { STATE_ERROR(state, PATHX_EPRED); return NULL; } skipws(state); } if (nexpr == 0) return NULL; if (ALLOC(pred) < 0) { STATE_ENOMEM; return NULL; } pred->nexpr = nexpr; if (ALLOC_N(pred->exprs, nexpr) < 0) { free_pred(pred); STATE_ENOMEM; return NULL; } for (int i = nexpr - 1; i >= 0; i--) pred->exprs[i] = pop_expr(state); return pred; } /* * Step ::= AxisSpecifier NameTest Predicate* | '.' | '..' * AxisSpecifier ::= AxisName '::' | * AxisName ::= 'ancestor' * | 'ancestor-or-self' * | 'child' * | 'descendant' * | 'descendant-or-self' * | 'parent' * | 'self' * | 'root' */ static struct step *parse_step(struct state *state) { struct step *step; int explicit_axis = 0, allow_predicates = 1; if (ALLOC(step) < 0) { STATE_ENOMEM; return NULL; } step->axis = CHILD; for (int i = 0; i < ARRAY_CARDINALITY(axis_names); i++) { if (looking_at(state, axis_names[i], "::")) { step->axis = i; explicit_axis = 1; break; } } if (! match(state, '*')) { step->name = parse_name(state); if (HAS_ERROR(state)) goto error; if (! explicit_axis) { if (STREQ(step->name, ".") || STREQ(step->name, "..")) { step->axis = STREQ(step->name, ".") ? SELF : PARENT; FREE(step->name); allow_predicates = 0; } } } if (allow_predicates) { step->predicates = parse_predicates(state); if (HAS_ERROR(state)) goto error; } return step; error: free_step(step); return NULL; } static struct step *make_step(enum axis axis, struct state *state) { struct step *result = NULL; if (ALLOC(result) < 0) { STATE_ENOMEM; return NULL; } result->axis = axis; return result; } /* * RelativeLocationPath ::= Step * | RelativeLocationPath '/' Step * | AbbreviatedRelativeLocationPath * AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step * * The above is the same as * RelativeLocationPath ::= Step ('/' Step | '//' Step)* */ static struct locpath * parse_relative_location_path(struct state *state) { struct step *step = NULL; struct locpath *locpath = NULL; step = parse_step(state); if (HAS_ERROR(state)) goto error; if (ALLOC(locpath) < 0) { STATE_ENOMEM; goto error; } list_append(locpath->steps, step); step = NULL; while (match(state, '/')) { if (*state->pos == '/') { state->pos += 1; step = make_step(DESCENDANT_OR_SELF, state); if (step == NULL) { STATE_ENOMEM; goto error; } list_append(locpath->steps, step); } step = parse_step(state); if (HAS_ERROR(state)) goto error; list_append(locpath->steps, step); step = NULL; } return locpath; error: free_step(step); free_locpath(locpath); return NULL; } /* * LocationPath ::= RelativeLocationPath | AbsoluteLocationPath * AbsoluteLocationPath ::= '/' RelativeLocationPath? * | AbbreviatedAbsoluteLocationPath * AbbreviatedAbsoluteLocationPath ::= '//' RelativeLocationPath * */ static void parse_location_path(struct state *state) { struct expr *expr = NULL; struct locpath *locpath = NULL; if (match(state, '/')) { if (*state->pos == '/') { state->pos += 1; locpath = parse_relative_location_path(state); if (HAS_ERROR(state)) goto error; struct step *step = make_step(DESCENDANT_OR_SELF, state); if (HAS_ERROR(state)) goto error; list_cons(locpath->steps, step); } else { if (*state->pos != '\0') { locpath = parse_relative_location_path(state); } else { if (ALLOC(locpath) < 0) goto err_nomem; } struct step *step = make_step(ROOT, state); if (HAS_ERROR(state)) { free_step(step); goto error; } list_cons(locpath->steps, step); } } else { locpath = parse_relative_location_path(state); } if (ALLOC(expr) < 0) goto err_nomem; expr->tag = E_FILTER; expr->locpath = locpath; push_expr(expr, state); return; err_nomem: STATE_ENOMEM; error: free_expr(expr); free_locpath(locpath); return; } /* * Number ::= /[0-9]+/ */ static void parse_number(struct state *state) { struct expr *expr = NULL; unsigned long val; char *end; errno = 0; val = strtoul(state->pos, &end, 10); if (errno || end == state->pos || (int) val != val) { STATE_ERROR(state, PATHX_ENUMBER); return; } state->pos = end; if (ALLOC(expr) < 0) goto err_nomem; expr->tag = E_VALUE; expr->value_ind = make_value(T_NUMBER, state); if (HAS_ERROR(state)) goto error; expr_value(expr, state)->number = val; push_expr(expr, state); return; err_nomem: STATE_ENOMEM; error: free_expr(expr); return; } /* * Literal ::= '"' /[^"]* / '"' | "'" /[^']* / "'" */ static void parse_literal(struct state *state) { char delim; const char *s; struct expr *expr = NULL; if (*state->pos == '"') delim = '"'; else if (*state->pos == '\'') delim = '\''; else { STATE_ERROR(state, PATHX_ESTRING); return; } state->pos += 1; s = state->pos; while (*state->pos != '\0' && *state->pos != delim) state->pos += 1; if (*state->pos != delim) { STATE_ERROR(state, PATHX_EDELIM); return; } state->pos += 1; if (ALLOC(expr) < 0) goto err_nomem; expr->tag = E_VALUE; expr->value_ind = make_value(T_STRING, state); if (HAS_ERROR(state)) goto error; expr_value(expr, state)->string = strndup(s, state->pos - s - 1); if (expr_value(expr, state)->string == NULL) goto err_nomem; push_expr(expr, state); return; err_nomem: STATE_ENOMEM; error: free_expr(expr); return; } /* * FunctionCall ::= Name '(' ( Expr ( ',' Expr )* )? ')' */ static void parse_function_call(struct state *state) { const struct func *func = NULL; struct expr *expr = NULL; int nargs = 0, find = 0; for (; find < ARRAY_CARDINALITY(builtin_funcs); find++) { if (looking_at(state, builtin_funcs[find].name, "(")) { func = builtin_funcs + find; break; } } if (func == NULL) { STATE_ERROR(state, PATHX_ENAME); return; } if (! match(state, ')')) { do { nargs += 1; parse_expr(state); RET_ON_ERROR; } while (match(state, ',')); if (! match(state, ')')) { STATE_ERROR(state, PATHX_EPAREN); return; } } int found = 0; /* Whether there is a builtin matching in name and arity */ for (int i=find; i < ARRAY_CARDINALITY(builtin_funcs); i++) { if (STRNEQ(func->name, builtin_funcs[i].name)) break; if (builtin_funcs[i].arity == nargs) { func = builtin_funcs + i; found = 1; break; } } if (! found) { STATE_ERROR(state, PATHX_EARITY); return; } if (ALLOC(expr) < 0) { STATE_ENOMEM; return; } expr->tag = E_APP; if (ALLOC_N(expr->args, nargs) < 0) { free_expr(expr); STATE_ENOMEM; return; } expr->func = func; for (int i = nargs - 1; i >= 0; i--) expr->args[i] = pop_expr(state); push_expr(expr, state); } /* * VariableReference ::= '$' /[a-zA-Z_][a-zA-Z0-9_]* / * * The '$' is consumed by parse_primary_expr */ static void parse_var(struct state *state) { const char *id = state->pos; struct expr *expr = NULL; if (!isalpha(*id) && *id != '_') { STATE_ERROR(state, PATHX_ENAME); return; } id++; while (isalpha(*id) || isdigit(*id) || *id == '_') id += 1; if (ALLOC(expr) < 0) goto err_nomem; expr->tag = E_VAR; expr->ident = strndup(state->pos, id - state->pos); if (expr->ident == NULL) goto err_nomem; push_expr(expr, state); state->pos = id; return; err_nomem: STATE_ENOMEM; free_expr(expr); return; } /* * PrimaryExpr ::= Literal * | Number * | FunctionCall * | VariableReference * | '(' Expr ')' * */ static void parse_primary_expr(struct state *state) { if (peek(state, "'\"")) { parse_literal(state); } else if (peek(state, "0123456789")) { parse_number(state); } else if (match(state, '(')) { parse_expr(state); RET_ON_ERROR; if (! match(state, ')')) { STATE_ERROR(state, PATHX_EPAREN); return; } } else if (match(state, '$')) { parse_var(state); } else { parse_function_call(state); } } static int looking_at_primary_expr(struct state *state) { const char *s = state->pos; /* Is it a Number, Literal or VariableReference ? */ if (peek(state, "$'\"0123456789")) return 1; /* Or maybe a function call, i.e. a word followed by a '(' ? * Note that our function names are only [a-zA-Z]+ */ while (*s != '\0' && isalpha(*s)) s++; while (*s != '\0' && isspace(*s)) s++; return *s == '('; } /* * PathExpr ::= LocationPath * | FilterExpr * | FilterExpr '/' RelativeLocationPath * | FilterExpr '//' RelativeLocationPath * * FilterExpr ::= PrimaryExpr Predicate * * The grammar is ambiguous here: the expression '42' can either be the * number 42 (a PrimaryExpr) or the RelativeLocationPath 'child::42'. The * reason for this ambiguity is that we allow node names like '42' in the * tree; rather than forbid them, we resolve the ambiguity by always * parsing '42' as a number, and requiring that the user write the * RelativeLocationPath in a different form, e.g. 'child::42' or './42'. */ static void parse_path_expr(struct state *state) { struct expr *expr = NULL; struct pred *predicates = NULL; struct locpath *locpath = NULL; if (looking_at_primary_expr(state)) { parse_primary_expr(state); RET_ON_ERROR; predicates = parse_predicates(state); RET_ON_ERROR; if (match(state, '/')) { if (match(state, '/')) { locpath = parse_relative_location_path(state); if (HAS_ERROR(state)) goto error; struct step *step = make_step(DESCENDANT_OR_SELF, state); if (HAS_ERROR(state)) return; list_cons(locpath->steps, step); } else { if (*state->pos == '\0') { STATE_ERROR(state, PATHX_EEND); goto error; } locpath = parse_relative_location_path(state); } } /* A PathExpr without predicates and locpath is * just a PrimaryExpr */ if (predicates == NULL && locpath == NULL) return; /* To make evaluation easier, we parse something like * $var[pred] as $var[pred]/. */ if (locpath == NULL) { if (ALLOC(locpath) < 0) goto error; if (ALLOC(locpath->steps) < 0) goto error; locpath->steps->axis = SELF; } if (ALLOC(expr) < 0) goto error; expr->tag = E_FILTER; expr->predicates = predicates; expr->primary = pop_expr(state); expr->locpath = locpath; push_expr(expr, state); } else { parse_location_path(state); } return; error: free_expr(expr); free_pred(predicates); free_locpath(locpath); return; } /* * UnionExpr ::= PathExpr ('|' PathExpr)* */ static void parse_union_expr(struct state *state) { parse_path_expr(state); RET_ON_ERROR; while (match(state, '|')) { parse_path_expr(state); RET_ON_ERROR; push_new_binary_op(OP_UNION, state); } } /* * MultiplicativeExpr ::= UnionExpr ('*' UnionExpr)* */ static void parse_multiplicative_expr(struct state *state) { parse_union_expr(state); RET_ON_ERROR; while (match(state, '*')) { parse_union_expr(state); RET_ON_ERROR; push_new_binary_op(OP_STAR, state); } } /* * AdditiveExpr ::= MultiplicativeExpr (AdditiveOp MultiplicativeExpr)* * AdditiveOp ::= '+' | '-' */ static void parse_additive_expr(struct state *state) { parse_multiplicative_expr(state); RET_ON_ERROR; while (*state->pos == '+' || *state->pos == '-') { enum binary_op op = (*state->pos == '+') ? OP_PLUS : OP_MINUS; state->pos += 1; skipws(state); parse_multiplicative_expr(state); RET_ON_ERROR; push_new_binary_op(op, state); } } /* * RelationalExpr ::= AdditiveExpr (RelationalOp AdditiveExpr)? * RelationalOp ::= ">" | "<" | ">=" | "<=" */ static void parse_relational_expr(struct state *state) { parse_additive_expr(state); RET_ON_ERROR; if (*state->pos == '<' || *state->pos == '>') { enum binary_op op = (*state->pos == '<') ? OP_LT : OP_GT; state->pos += 1; if (*state->pos == '=') { op = (op == OP_LT) ? OP_LE : OP_GE; state->pos += 1; } skipws(state); parse_additive_expr(state); RET_ON_ERROR; push_new_binary_op(op, state); } } /* * EqualityExpr ::= RelationalExpr (EqualityOp RelationalExpr)? | ReMatchExpr * EqualityOp ::= "=" | "!=" * ReMatchExpr ::= RelationalExpr MatchOp RelationalExpr * MatchOp ::= "=~" | "!~" */ static void parse_equality_expr(struct state *state) { parse_relational_expr(state); RET_ON_ERROR; if ((*state->pos == '=' || *state->pos == '!') && state->pos[1] == '~') { enum binary_op op = (*state->pos == '=') ? OP_RE_MATCH : OP_RE_NOMATCH; state->pos += 2; skipws(state); parse_relational_expr(state); RET_ON_ERROR; push_new_binary_op(op, state); } else if (*state->pos == '=' || (*state->pos == '!' && state->pos[1] == '=')) { enum binary_op op = (*state->pos == '=') ? OP_EQ : OP_NEQ; state->pos += (op == OP_EQ) ? 1 : 2; skipws(state); parse_relational_expr(state); RET_ON_ERROR; push_new_binary_op(op, state); } } /* * AndExpr ::= EqualityExpr ('and' EqualityExpr)* */ static void parse_and_expr(struct state *state) { parse_equality_expr(state); RET_ON_ERROR; while (*state->pos == 'a' && state->pos[1] == 'n' && state->pos[2] == 'd') { state->pos += 3; skipws(state); parse_equality_expr(state); RET_ON_ERROR; push_new_binary_op(OP_AND, state); } } /* * OrExpr ::= AndExpr ('or' AndExpr)* */ static void parse_or_expr(struct state *state) { parse_and_expr(state); RET_ON_ERROR; while (*state->pos == 'o' && state->pos[1] == 'r') { state->pos += 2; skipws(state); parse_and_expr(state); RET_ON_ERROR; push_new_binary_op(OP_OR, state); } } /* * ElseExpr ::= OrExpr ('else' OrExpr)* */ static void parse_else_expr(struct state *state) { parse_or_expr(state); RET_ON_ERROR; while (*state->pos == 'e' && state->pos[1] == 'l' && state->pos[2] == 's' && state->pos[3] == 'e' ) { state->pos += 4; skipws(state); parse_or_expr(state); RET_ON_ERROR; push_new_binary_op(OP_ELSE, state); state->has_else = 1; } } /* * Expr ::= ElseExpr */ static void parse_expr(struct state *state) { skipws(state); parse_else_expr(state); } static void store_error(struct pathx *pathx) { const char *pathx_msg = NULL; const char *path = pathx->state->txt; const pathx_errcode_t errcode = pathx->state->errcode; struct error *err = pathx->state->error; char *pos_str = pathx->state->errmsg; pathx->state->errmsg = NULL; if (err == NULL || errcode == PATHX_NOERROR || err->code != AUG_NOERROR) return; switch (errcode) { case PATHX_ENOMEM: err->code = AUG_ENOMEM; break; case PATHX_EMMATCH: err->code = AUG_EMMATCH; break; case PATHX_ENOMATCH: err->code = AUG_ENOMATCH; break; default: err->code = AUG_EPATHX; break; } /* We only need details for pathx syntax errors */ if (err->code != AUG_EPATHX) return; int pos; pathx_msg = pathx_error(pathx, NULL, &pos); bool has_msg = pos_str != NULL; int pos_str_len = pos_str == NULL ? 0 : strlen(pos_str); if (REALLOC_N(pos_str, pos_str_len + strlen(path) + 8) >= 0) { if (has_msg) { strcat(pos_str, " in "); strncat(pos_str, path, pos); } else { /* initialize pos_str explicitly, path might be "" */ pos_str[0] = '\0'; strncat(pos_str, path, pos); } strcat(pos_str, "|=|"); strcat(pos_str, path + pos); } err->minor = errcode; err->details = pos_str; pos_str = NULL; err->minor_details = pathx_msg; } int pathx_parse(const struct tree *tree, struct error *err, const char *txt, bool need_nodeset, struct pathx_symtab *symtab, struct tree *root_ctx, struct pathx **pathx) { struct state *state = NULL; *pathx = NULL; if (ALLOC(*pathx) < 0) goto oom; (*pathx)->origin = (struct tree *) tree; /* Set up state */ if (ALLOC((*pathx)->state) < 0) goto oom; state = (*pathx)->state; state->errcode = PATHX_NOERROR; state->errmsg = NULL; state->txt = txt; state->pos = txt; state->symtab = symtab; state->root_ctx = root_ctx; state->error = err; if (ALLOC_N(state->value_pool, 8) < 0) { STATE_ENOMEM; goto done; } state->value_pool_size = 8; state->value_pool[0].tag = T_BOOLEAN; state->value_pool[0].boolval = 0; state->value_pool[1].tag = T_BOOLEAN; state->value_pool[1].boolval = 1; state->value_pool_used = 2; /* Parse */ parse_expr(state); if (HAS_ERROR(state)) goto done; if (state->pos != state->txt + strlen(state->txt)) { STATE_ERROR(state, PATHX_EEND); goto done; } if (state->exprs_used != 1) { STATE_ERROR(state, PATHX_EINTERNAL); goto done; } /* Typecheck */ check_expr(state->exprs[0], state); if (HAS_ERROR(state)) goto done; if (need_nodeset && state->exprs[0]->type != T_NODESET) { STATE_ERROR(state, PATHX_ETYPE); goto done; } done: store_error(*pathx); return state->errcode; oom: free_pathx(*pathx); *pathx = NULL; if (err != NULL) err->code = AUG_ENOMEM; return PATHX_ENOMEM; } /************************************************************************* * Searching in the tree *************************************************************************/ static bool step_matches(struct step *step, struct tree *tree) { if ( step->axis == SEQ && step->name == NULL ) { if ( tree->label == NULL ) return false; /* label matches if it consists of numeric digits only */ for( char *s = tree->label; *s ; s++) { if ( ! isdigit(*s) ) return false; } return true; } else if (step->name == NULL) { return step->axis == ROOT || tree->label != NULL; } else { return streqx(step->name, tree->label); } } static struct tree *tree_prev(struct tree *pos) { struct tree *node = NULL; if (pos != pos->parent->children) { for (node = pos->parent->children; node->next != pos; node = node->next); } return node; } /* When the first step doesn't begin with ROOT then use relative root context * instead. */ static struct tree *step_root(struct step *step, struct tree *ctx, struct tree *root_ctx) { struct tree *node = NULL; switch (step->axis) { case SELF: case CHILD: case SEQ: case DESCENDANT: case PARENT: case ANCESTOR: case PRECEDING_SIBLING: case FOLLOWING_SIBLING: /* only use root_ctx when ctx is the absolute tree root */ if (ctx == ctx->parent && root_ctx != NULL) node = root_ctx; else node = ctx; break; case ROOT: case DESCENDANT_OR_SELF: node = ctx; break; default: assert(0); } if (node == NULL) return NULL; return node; } static struct tree *step_first(struct step *step, struct tree *ctx) { struct tree *node = NULL; switch (step->axis) { case SELF: case DESCENDANT_OR_SELF: node = ctx; break; case CHILD: case SEQ: case DESCENDANT: node = ctx->children; break; case PARENT: case ANCESTOR: node = ctx->parent; break; case ROOT: while (ctx->parent != ctx) ctx = ctx->parent; node = ctx; break; case PRECEDING_SIBLING: node = tree_prev(ctx); break; case FOLLOWING_SIBLING: node = ctx->next; break; default: assert(0); } if (node == NULL) return NULL; if (step_matches(step, node)) return node; return step_next(step, ctx, node); } static struct tree *step_next(struct step *step, struct tree *ctx, struct tree *node) { while (node != NULL) { switch (step->axis) { case SELF: node = NULL; break; case SEQ: case CHILD: node = node->next; break; case DESCENDANT: case DESCENDANT_OR_SELF: if (node->children != NULL) { node = node->children; } else { while (node->next == NULL && node != ctx) node = node->parent; if (node == ctx) node = NULL; else node = node->next; } break; case PARENT: case ROOT: node = NULL; break; case ANCESTOR: if (node->parent == node) node = NULL; else node = node->parent; break; case PRECEDING_SIBLING: node = tree_prev(node); break; case FOLLOWING_SIBLING: node = node->next; break; default: assert(0); } if (node != NULL && step_matches(step, node)) break; } return node; } static struct value *pathx_eval(struct pathx *pathx) { struct state *state = pathx->state; state->ctx = pathx->origin; state->ctx_pos = 1; state->ctx_len = 1; eval_expr(state->exprs[0], state); if (HAS_ERROR(state)) return NULL; if (state->values_used != 1) { STATE_ERROR(state, PATHX_EINTERNAL); return NULL; } return pop_value(state); } struct tree *pathx_next(struct pathx *pathx) { if (pathx->node + 1 < pathx->nodeset->used) return pathx->nodeset->nodes[++pathx->node]; return NULL; } /* Find the first node in TREE matching PATH. */ struct tree *pathx_first(struct pathx *pathx) { if (pathx->nodeset == NULL) { struct value *v = pathx_eval(pathx); if (HAS_ERROR(pathx->state)) goto error; assert(v->tag == T_NODESET); pathx->nodeset = v->nodeset; } pathx->node = 0; if (pathx->nodeset->used == 0) return NULL; else return pathx->nodeset->nodes[0]; error: store_error(pathx); return NULL; } /* Find a node in the tree that matches the longest prefix of PATH. * * Return 1 if a node was found that exactly matches PATH, 0 if an incomplete * prefix matches, and -1 if more than one node in the tree match. * * TMATCH is set to the tree node that matches, and SMATCH to the next step * after the one where TMATCH matched. If no node matches or multiple nodes * at the same depth match, TMATCH and SMATCH will be NULL. When exactly * one node matches, TMATCH will be that node, and SMATCH will be NULL. */ static int locpath_search(struct locpath_trace *lpt, struct tree **tmatch, struct step **smatch) { int last; int result = -1; for (last=lpt->maxns; last >= 0 && lpt->ns[last]->used == 0; last--); if (last < 0) { *smatch = lpt->lp->steps; result = 1; goto done; } if (lpt->ns[last]->used > 1) { result = -1; goto done; } result = 0; *tmatch = lpt->ns[last]->nodes[0]; *smatch = lpt->lp->steps; for (int i=0; i < last; i++) *smatch = (*smatch)->next; done: for (int i=0; i < lpt->maxns; i++) free_nodeset(lpt->ns[i]); FREE(lpt->ns); return result; } static char *step_seq_choose_name(struct pathx *path, struct tree *tree); /* Expand the tree ROOT so that it contains all components of PATH. PATH * must have been initialized against ROOT by a call to PATH_FIND_ONE. * * Return the first segment that was created by this operation, or NULL on * error. */ int pathx_expand_tree(struct pathx *path, struct tree **tree) { int r; struct step *step = NULL; struct locpath_trace lpt; struct tree *first_child = NULL; struct value *v = NULL; MEMZERO(&lpt, 1); path->state->locpath_trace = &lpt; v = pathx_eval(path); path->state->locpath_trace = NULL; if (HAS_ERROR(path->state)) goto error; if (lpt.maxns == 0) { if (v->tag != T_NODESET || v->nodeset->used == 0) { STATE_ERROR(path->state, PATHX_ENOMATCH); goto error; } if (v->nodeset->used > 1) goto error; *tree = v->nodeset->nodes[0]; return 0; } *tree = path->origin; r = locpath_search(&lpt, tree, &step); if (r == -1) { STATE_ERROR(path->state, PATHX_EMMATCH); goto error; } if (step == NULL) return 0; struct tree *parent = *tree; if (parent == NULL) parent = path->origin; list_for_each(s, step) { if (s->axis != CHILD && s->axis != SEQ) goto error; if (s->axis==SEQ && s->name == NULL) { s->name = step_seq_choose_name(path, parent); } if (s->name == NULL ) goto error; struct tree *t = make_tree(strdup(s->name), NULL, parent, NULL); if (first_child == NULL) first_child = t; if (t == NULL || t->label == NULL) goto error; list_append(parent->children, t); parent = t; } while (first_child->children != NULL) first_child = first_child->children; *tree = first_child; return 1; error: if (first_child != NULL) { list_remove(first_child, first_child->parent->children); free_tree(first_child); } *tree = NULL; store_error(path); return -1; } /* Generate a numeric string to use for step->name * Scan tree->children for the highest numbered label, and add 1 to that * numeric labels may be interspersed with #comment or other labels */ static char *step_seq_choose_name(struct pathx *path, struct tree *tree) { unsigned long int max_node_n=0; unsigned long int node_n; char *step_name; char *label_end; for(tree=tree->children; tree!=NULL; tree=tree->next) { if ( tree->label == NULL) continue; node_n=strtoul(tree->label, &label_end, 10); if ( label_end == tree->label || *label_end != '\0' ) /* label is not a number - ignore it */ continue; if ( node_n >= ULONG_MAX ) { STATE_ERROR(path->state, PATHX_ENUMBER); return NULL; } if( node_n > max_node_n ) max_node_n = node_n; } if (asprintf(&step_name,"%lu",max_node_n+1) >= 0) return step_name; else return NULL; } int pathx_find_one(struct pathx *path, struct tree **tree) { *tree = pathx_first(path); if (HAS_ERROR(path->state)) return -1; return path->nodeset->used; } struct error *err_of_pathx(struct pathx *px) { return px->state->error; } const char *pathx_error(struct pathx *path, const char **txt, int *pos) { int errcode = PATHX_ENOMEM; if (path != NULL) { if (path->state->errcode < ARRAY_CARDINALITY(errcodes)) errcode = path->state->errcode; else errcode = PATHX_EINTERNAL; if (txt) *txt = path->state->txt; if (pos) *pos = path->state->pos - path->state->txt; } return errcodes[errcode]; } /* * Symbol tables */ static struct pathx_symtab *make_symtab(struct pathx_symtab *symtab, const char *name, struct value *value) { struct pathx_symtab *new; char *n = NULL; n = strdup(name); if (n == NULL) return NULL; if (ALLOC(new) < 0) { free(n); return NULL; } new->name = n; new->value = value; if (symtab == NULL) { return new; } else { new->next = symtab->next; symtab->next = new; } return symtab; } void free_symtab(struct pathx_symtab *symtab) { while (symtab != NULL) { struct pathx_symtab *del = symtab; symtab = del->next; free(del->name); release_value(del->value); free(del->value); free(del); } } struct pathx_symtab *pathx_get_symtab(struct pathx *pathx) { return pathx->state->symtab; } static int pathx_symtab_set(struct pathx_symtab **symtab, const char *name, struct value *v) { int found = 0; list_for_each(tab, *symtab) { if (STREQ(tab->name, name)) { release_value(tab->value); free(tab->value); tab->value = v; found = 1; break; } } if (!found) { struct pathx_symtab *new = NULL; new = make_symtab(*symtab, name, v); if (new == NULL) goto error; *symtab = new; } return 0; error: return -1; } int pathx_symtab_define(struct pathx_symtab **symtab, const char *name, struct pathx *px) { int r; struct value *value = NULL, *v = NULL; struct state *state = px->state; value = pathx_eval(px); if (HAS_ERROR(px->state)) goto error; if (ALLOC(v) < 0) { STATE_ENOMEM; goto error; } *v = *value; value->tag = T_BOOLEAN; r = pathx_symtab_set(symtab, name, v); if (r < 0) { STATE_ENOMEM; goto error; } if (v->tag == T_NODESET) return v->nodeset->used; else return 0; error: release_value(value); free(value); release_value(v); free(v); store_error(px); return -1; } int pathx_symtab_undefine(struct pathx_symtab **symtab, const char *name) { struct pathx_symtab *del = NULL; for(del = *symtab; del != NULL && !STREQ(del->name, name); del = del->next); if (del == NULL) return 0; list_remove(del, *symtab); free_symtab(del); return 0; } int pathx_symtab_assign_tree(struct pathx_symtab **symtab, const char *name, struct tree *tree) { struct value *v = NULL; int r; if (ALLOC(v) < 0) goto error; v->tag = T_NODESET; if (ALLOC(v->nodeset) < 0) goto error; if (ALLOC_N(v->nodeset->nodes, 1) < 0) goto error; v->nodeset->used = 1; v->nodeset->size = 1; v->nodeset->nodes[0] = tree; r = pathx_symtab_set(symtab, name, v); if (r < 0) goto error; return 1; error: release_value(v); free(v); return -1; } int pathx_symtab_count(const struct pathx_symtab *symtab, const char *name) { struct value *v = lookup_var(name, symtab); if (v == NULL || v->tag != T_NODESET) return -1; return v->nodeset->used; } struct tree * pathx_symtab_get_tree(struct pathx_symtab *symtab, const char *name, int i) { struct value *v = lookup_var(name, symtab); if (v == NULL) return NULL; if (v->tag != T_NODESET) return NULL; if (i >= v->nodeset->used) return NULL; return v->nodeset->nodes[i]; } void pathx_symtab_remove_descendants(struct pathx_symtab *symtab, const struct tree *tree) { list_for_each(tab, symtab) { if (tab->value->tag != T_NODESET) continue; struct nodeset *ns = tab->value->nodeset; for (int i=0; i < ns->used;) { struct tree *t = ns->nodes[i]; while (t != t->parent && t != tree) t = t->parent; if (t == tree) ns_remove(ns, i, 1); else i += 1; } } } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/internal.h0000644000175000017500000005514114161102026012535 00000000000000/* * internal.h: Useful definitions * * Copyright (C) 2007-2017 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #ifndef INTERNAL_H_ #define INTERNAL_H_ #include "list.h" #include "datadir.h" #include "augeas.h" #include #include #include #include #include #include #include #include #include #include /* * Various parameters about env vars, special tree nodes etc. */ /* Define: AUGEAS_LENS_DIR * The default location for lens definitions */ #define AUGEAS_LENS_DIR DATADIR "/augeas/lenses" /* The directory where we install lenses distribute with Augeas */ #define AUGEAS_LENS_DIST_DIR DATADIR "/augeas/lenses/dist" /* Define: AUGEAS_ROOT_ENV * The env var that points to the chroot holding files we may modify. * Mostly useful for testing */ #define AUGEAS_ROOT_ENV "AUGEAS_ROOT" /* Define: AUGEAS_FILES_TREE * The root for actual file contents */ #define AUGEAS_FILES_TREE "/files" /* Define: AUGEAS_META_TREE * Augeas reports some information in this subtree */ #define AUGEAS_META_TREE "/augeas" /* Define: AUGEAS_META_FILES * Information about files */ #define AUGEAS_META_FILES AUGEAS_META_TREE AUGEAS_FILES_TREE /* Define: AUGEAS_META_TEXT * Information about text (see aug_text_store and aug_text_retrieve) */ #define AUGEAS_META_TEXT AUGEAS_META_TREE "/text" /* Define: AUGEAS_META_ROOT * The root directory */ #define AUGEAS_META_ROOT AUGEAS_META_TREE "/root" /* Define: AUGEAS_META_SAVE_MODE * How we save files. One of 'backup', 'overwrite' or 'newfile' */ #define AUGEAS_META_SAVE_MODE AUGEAS_META_TREE "/save" /* Define: AUGEAS_CLONE_IF_RENAME_FAILS * Control what save does when renaming the temporary file to its final * destination fails with EXDEV or EBUSY: when this tree node exists, copy * the file contents. If it is not present, simply give up and report an * error. */ #define AUGEAS_COPY_IF_RENAME_FAILS \ AUGEAS_META_SAVE_MODE "/copy_if_rename_fails" /* Define: AUGEAS_CONTEXT * Context prepended to all non-absolute paths */ #define AUGEAS_CONTEXT AUGEAS_META_TREE "/context" /* A hierarchy where we record certain 'events', e.g. which tree * nodes actually gotsaved into files */ #define AUGEAS_EVENTS AUGEAS_META_TREE "/events" #define AUGEAS_EVENTS_SAVED AUGEAS_EVENTS "/saved" /* Where to put information about parsing of path expressions */ #define AUGEAS_META_PATHX AUGEAS_META_TREE "/pathx" /* Define: AUGEAS_SPAN_OPTION * Enable or disable node indexes */ #define AUGEAS_SPAN_OPTION AUGEAS_META_TREE "/span" /* Define: AUGEAS_LENS_ENV * Name of env var that contains list of paths to search for additional spec files */ #define AUGEAS_LENS_ENV "AUGEAS_LENS_LIB" /* Define: MAX_ENV_SIZE * Fairly arbitrary bound on the length of the path we * accept from AUGEAS_SPEC_ENV */ #define MAX_ENV_SIZE 4096 /* Define: PATH_SEP_CHAR * Character separating paths in a list of paths */ #define PATH_SEP_CHAR ':' /* Constants for setting the save mode via the augeas path at * AUGEAS_META_SAVE_MODE */ #define AUG_SAVE_BACKUP_TEXT "backup" #define AUG_SAVE_NEWFILE_TEXT "newfile" #define AUG_SAVE_NOOP_TEXT "noop" #define AUG_SAVE_OVERWRITE_TEXT "overwrite" /* constants for options in the tree */ #define AUG_ENABLE "enable" #define AUG_DISABLE "disable" /* default value for the relative path context */ #define AUG_CONTEXT_DEFAULT "/files" #ifdef __GNUC__ #ifndef __GNUC_PREREQ #define __GNUC_PREREQ(maj,min) 0 #endif /** * AUGEAS_LIKELY: * * Macro to flag a code branch as a likely branch * * AUGEAS_UNLIKELY: * * Macro to flag a code branch as an unlikely branch */ #ifndef __has_builtin # define __has_builtin(x) (0) #endif #if defined(__builtin_expect) || __has_builtin(__builtin_expect) # define AUGEAS_LIKELY(x) (__builtin_expect(!!(x), 1)) # define AUGEAS_UNLIKELY(x) (__builtin_expect(!!(x), 0)) #else # define AUGEAS_LIKELY(x) (x) # define AUGEAS_UNLIKELY(x) (x) #endif /** * ATTRIBUTE_UNUSED: * * Macro to flag conciously unused parameters to functions */ #ifndef ATTRIBUTE_UNUSED #define ATTRIBUTE_UNUSED __attribute__((__unused__)) #endif /** * ATTRIBUTE_FORMAT * * Macro used to check printf/scanf-like functions, if compiling * with gcc. */ #ifndef ATTRIBUTE_FORMAT #define ATTRIBUTE_FORMAT(args...) __attribute__((__format__ (args))) #endif #ifndef ATTRIBUTE_PURE #define ATTRIBUTE_PURE __attribute__((pure)) #endif #ifndef ATTRIBUTE_RETURN_CHECK #if __GNUC_PREREQ (3, 4) #define ATTRIBUTE_RETURN_CHECK __attribute__((__warn_unused_result__)) #else #define ATTRIBUTE_RETURN_CHECK #endif #endif /* Allow falling through in switch statements for the few cases where that is needed */ #ifndef ATTRIBUTE_FALLTHROUGH # if __GNUC_PREREQ (7, 0) # define ATTRIBUTE_FALLTHROUGH __attribute__ ((fallthrough)) # else # define ATTRIBUTE_FALLTHROUGH # endif #endif /* A poor man's macro to get some move semantics: return the value of P but set P itself to NULL. This has the effect that if you say 'x = move(y)' that there is still only one pointer pointing to the memory Y pointed to initially. */ #define move(p) ({ typeof(p) _tmp = (p); (p) = NULL; _tmp; }) #else #define ATTRIBUTE_UNUSED #define ATTRIBUTE_FORMAT(...) #define ATTRIBUTE_PURE #define ATTRIBUTE_RETURN_CHECK #define ATTRIBUTE_FALLTHROUGH #define move(p) p #endif /* __GNUC__ */ #define ARRAY_CARDINALITY(array) (sizeof (array) / sizeof *(array)) /* String equality tests, suggested by Jim Meyering. */ #define STREQ(a,b) (strcmp((a),(b)) == 0) #define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0) #define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0) #define STRNEQ(a,b) (strcmp((a),(b)) != 0) #define STRCASENEQ(a,b) (strcasecmp((a),(b)) != 0) #define STREQLEN(a,b,n) (strncmp((a),(b),(n)) == 0) #define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0) ATTRIBUTE_PURE static inline int streqv(const char *a, const char *b) { if (a == NULL || b == NULL) return a == b; return STREQ(a,b); } /* Path length and comparison */ #define SEP '/' /* Length of PATH without any trailing '/' */ ATTRIBUTE_PURE static inline int pathlen(const char *path) { int len = strlen(path); if (len > 0 && path[len-1] == SEP) len--; return len; } /* Return 1 if P1 is a prefix of P2. P1 as a string must have length <= P2 */ ATTRIBUTE_PURE static inline int pathprefix(const char *p1, const char *p2) { if (p1 == NULL || p2 == NULL) return 0; int l1 = pathlen(p1); return STREQLEN(p1, p2, l1) && (p2[l1] == '\0' || p2[l1] == SEP); } static inline int pathendswith(const char *path, const char *basenam) { const char *p = strrchr(path, SEP); if (p == NULL) return 0; return streqv(p+1, basenam); } /* Join NSEG path components (passed as const char *) into one PATH. Allocate as needed. Return 0 on success, -1 on failure */ int pathjoin(char **path, int nseg, ...); #define MEMZERO(ptr, n) memset((ptr), 0, (n) * sizeof(*(ptr))); #define MEMMOVE(dest, src, n) memmove((dest), (src), (n) * sizeof(*(src))) /** * TODO: * * macro to flag unimplemented blocks */ #define TODO \ fprintf(stderr, "%s:%d Unimplemented block\n", \ __FILE__, __LINE__); #define FIXME(msg, args ...) \ do { \ fprintf(stderr, "%s:%d Fixme: ", \ __FILE__, __LINE__); \ fprintf(stderr, msg, ## args); \ fputc('\n', stderr); \ } while(0) /* * Internal data structures */ // internal.c /* Function: escape * Escape nonprintable characters within TEXT, similar to how it's done in * C string literals. Caller must free the returned string. */ char *escape(const char *text, int cnt, const char *extra); /* Function: unescape */ char *unescape(const char *s, int len, const char *extra); /* Extra characters to be escaped in strings and regexps respectively */ #define STR_ESCAPES "\"\\" #define RX_ESCAPES "/\\" /* Function: print_chars */ int print_chars(FILE *out, const char *text, int cnt); /* Function: print_pos * Print a pretty representation of being at position POS within TEXT */ void print_pos(FILE *out, const char *text, int pos); char *format_pos(const char *text, int pos); /* Function: xread_file * Read the contents of file PATH and return them as one long string. The * caller must free the result. Return NULL if any error occurs. */ char* xread_file(const char *path); /* Like xread_file, but caller supplies a file pointer */ char* xfread_file(FILE *fp); /* Get the error message for ERRNUM in a threadsafe way. Based on libvirt's * virStrError */ const char *xstrerror(int errnum, char *buf, size_t len); /* Like asprintf, but set *STRP to NULL on error */ int xasprintf(char **strp, const char *format, ...); /* Convert S to RESULT with error checking */ int xstrtoint64(char const *s, int base, int64_t *result); /* Calculate line and column number of character POS in TEXT */ void calc_line_ofs(const char *text, size_t pos, size_t *line, size_t *ofs); /* Cleans path from user, removing trailing slashes and whitespace */ char *cleanpath(char *path); /* Take the first LEN characters from the regexp *U and expand any * character ranges in it. The expanded regexp, if expansion is necessary, * is in U, and the old string is freed. If expansion is not needed or an * error happens, U will be unchanged. * * Return 0 if expansion is not necessary, -1 if an error occurs, and 1 if * expansion was needed. */ int regexp_c_locale(char **u, size_t *len); /* Struct: augeas * The data structure representing a connection to Augeas. */ struct augeas { struct tree *origin; /* Actual tree root is origin->children */ const char *root; /* Filesystem root for all files */ /* always ends with '/' */ unsigned int flags; /* Flags passed to AUG_INIT */ struct module *modules; /* Loaded modules */ size_t nmodpath; char *modpathz; /* The search path for modules as a glibc argz vector */ struct pathx_symtab *symtab; struct error *error; uint api_entries; /* Number of entries through a public * API, 0 when called from outside */ #if HAVE_USELOCALE /* On systems that have a uselocale call, we switch to the C locale * on entry into API functions, and back to the old user locale * on exit. * FIXME: We need some solution for systems without uselocale, like * setlocale + critical section, though that is very heavy-handed */ locale_t c_locale; locale_t user_locale; #endif }; static inline struct error *err_of_aug(const struct augeas *aug) { return ((struct augeas *) aug)->error; } /* Used by augparse for loading tests */ int __aug_load_module_file(struct augeas *aug, const char *filename); /* Called at beginning and end of every _public_ API function */ void api_entry(const struct augeas *aug); void api_exit(const struct augeas *aug); /* Struct: tree * An entry in the global config tree. The data structure allows associating * values with interior nodes, but the API currently marks that as an error. * * To make dealing with parents uniform, even for the root, we create * standalone trees with a fake root, called origin. That root is generally * not referenced from anywhere. Standalone trees should be created with * MAKE_TREE_ORIGIN. * * The DIRTY flag is used to track which parts of the tree might need to be * saved. For any node that is marked dirty, all of its ancestors must be * marked dirty, too. Instead of setting this flag directly, the function * TREE_MARK_DIRTY in augeas.c should be used (and only functions in that * file should have a need to mark nodes as dirty) * * The FILE flag is set for entries underneath /augeas/files that hold the * metadata for a file by ADD_FILE_INFO. The FILE flag is set for entries * underneath /files for the toplevel node corresponding to a file by * TREE_FREPLACE and is used by AUG_SOURCE to find the file to which a node * belongs. */ struct tree { struct tree *next; struct tree *parent; /* Points to self for root */ char *label; /* Last component of PATH */ struct tree *children; /* List of children through NEXT */ char *value; struct span *span; /* Flags */ bool dirty; bool file; bool added; /* only used by ns_add and tree_rm to dedupe nodesets */ }; /* The opaque structure used to represent path expressions. API's * using STRUCT PATHX are declared farther below */ struct pathx; #define ROOT_P(t) ((t) != NULL && (t)->parent == (t)->parent->parent) #define TREE_HIDDEN(tree) ((tree)->label == NULL) /* Function: make_tree * Allocate a new tree node with the given LABEL, VALUE, and CHILDREN, * which are not copied. The new tree is marked as dirty */ struct tree *make_tree(char *label, char *value, struct tree *parent, struct tree *children); /* Mark a tree as a standalone tree; this creates a fake parent for ROOT, * so that even ROOT has a parent. A new node with only child ROOT is * returned on success, and NULL on failure. */ struct tree *make_tree_origin(struct tree *root); /* Make a new tree node and append it to parent's children */ struct tree *tree_append(struct tree *parent, char *label, char *value); int tree_rm(struct pathx *p); int tree_unlink(struct augeas *aug, struct tree *tree); struct tree *tree_set(struct pathx *p, const char *value); int tree_insert(struct pathx *p, const char *label, int before); int free_tree(struct tree *tree); int dump_tree(FILE *out, struct tree *tree); int tree_equal(const struct tree *t1, const struct tree *t2); /* Return the 1-based index of TREE amongst its siblings with the same * label or 0 if none of TREE's siblings have the same label */ int tree_sibling_index(struct tree *tree); char *path_expand(struct tree *tree, const char *ppath); char *path_of_tree(struct tree *tree); /* Clear the dirty flag in the whole TREE */ void tree_clean(struct tree *tree); /* Return first child with label LABEL or NULL */ struct tree *tree_child(struct tree *tree, const char *label); /* Return first existing child with label LABEL or create one. Return NULL * when allocation fails */ struct tree *tree_child_cr(struct tree *tree, const char *label); /* Create a path in the tree; nodes along the path are looked up with * tree_child_cr */ struct tree *tree_path_cr(struct tree *tree, int n, ...); /* Store VALUE directly as the value of TREE and set VALUE to NULL. * Update dirty flags */ void tree_store_value(struct tree *tree, char **value); /* Set the value of TREE to a copy of VALUE and update dirty flags */ int tree_set_value(struct tree *tree, const char *value); /* Cleanly remove all children of TREE, but leave TREE itself unchanged */ void tree_unlink_children(struct augeas *aug, struct tree *tree); /* Find a node in the tree at path FPATH; FPATH is a file path, i.e. * not interpreted as a path expression. If no such node exists, return NULL */ struct tree *tree_fpath(struct augeas *aug, const char *fpath); /* Find a node in the tree at path FPATH; FPATH is a file path, i.e. * not interpreted as a path expression. If no such node exists, create * it and all its missing ancestors. */ struct tree *tree_fpath_cr(struct augeas *aug, const char *fpath); /* Find the node matching PATH. * Returns the node or NULL on error * Errors: EMMATCH - more than one node matches PATH * ENOMEM - allocation error */ struct tree *tree_find(struct augeas *aug, const char *path); /* Find the node matching PATH. Expand the tree to contain such a node if * none exists. * Returns the node or NULL on error */ struct tree *tree_find_cr(struct augeas *aug, const char *path); /* Find the node at the path stored in AUGEAS_CONTEXT, i.e. the root context * node for relative paths. * Errors: EMMATCH - more than one node matches PATH * ENOMEM - allocation error */ struct tree *tree_root_ctx(const struct augeas *aug); /* Struct: memstream * Wrappers to simulate OPEN_MEMSTREAM where that's not available. The * STREAM member is opened by INIT_MEMSTREAM and closed by * CLOSE_MEMSTREAM. The BUF is allocated automatically, but can not be used * until after CLOSE_MEMSTREAM has been called. It is the callers * responsibility to free up BUF. */ struct memstream { FILE *stream; char *buf; size_t size; }; /* Function: init_memstream * Initialize a memstream. On systems that have OPEN_MEMSTREAM, it is used * to open MS->STREAM. On systems without OPEN_MEMSTREAM, MS->STREAM is * backed by a temporary file. * * MS must be allocated in advance; INIT_MEMSTREAM initializes it. */ int __aug_init_memstream(struct memstream *ms); #define init_memstream(ms) __aug_init_memstream(ms); /* Function: close_memstream * Close a memstream. After calling this, MS->STREAM can not be used * anymore and a string representing whatever was written to it is * available in MS->BUF. The caller must free MS->BUF. * * The caller must free the MEMSTREAM structure. */ int __aug_close_memstream(struct memstream *ms); #define close_memstream(ms) __aug_close_memstream(ms) /* * Path expressions */ typedef enum { PATHX_NOERROR = 0, PATHX_ENAME, PATHX_ESTRING, PATHX_ENUMBER, PATHX_EDELIM, PATHX_ENOEQUAL, PATHX_ENOMEM, PATHX_EPRED, PATHX_EPAREN, PATHX_ESLASH, PATHX_EINTERNAL, PATHX_ETYPE, PATHX_ENOVAR, PATHX_EEND, PATHX_ENOMATCH, PATHX_EARITY, PATHX_EREGEXP, PATHX_EMMATCH, PATHX_EREGEXPFLAG } pathx_errcode_t; struct pathx; struct pathx_symtab; const char *pathx_error(struct pathx *pathx, const char **txt, int *pos); /* Parse a path expression PATH rooted at TREE, which is a node somewhere * in AUG->ORIGIN. If TREE is NULL, AUG->ORIGIN is used. If ROOT_CTX is not * NULL and the PATH isn't absolute then it will be rooted at ROOT_CTX. * * Use this function rather than PATHX_PARSE for path expressions inside * the tree in AUG->ORIGIN. * * If NEED_NODESET is true, the resulting path expression must evaluate toa * nodeset, otherwise it can evaluate to a value of any type. * * Return the resulting path expression, or NULL on error. If an error * occurs, the error struct in AUG contains details. */ struct pathx *pathx_aug_parse(const struct augeas *aug, struct tree *tree, struct tree *root_ctx, const char *path, bool need_nodeset); /* Parse the string PATH into a path expression PX that will be evaluated * against the tree ORIGIN. * * If NEED_NODESET is true, the resulting path expression must evaluate toa * nodeset, otherwise it can evaluate to a value of any type. * * Returns 0 on success, and -1 on error */ int pathx_parse(const struct tree *origin, struct error *err, const char *path, bool need_nodeset, struct pathx_symtab *symtab, struct tree *root_ctx, struct pathx **px); /* Return the error struct that was passed into pathx_parse */ struct error *err_of_pathx(struct pathx *px); struct tree *pathx_first(struct pathx *path); struct tree *pathx_next(struct pathx *path); /* Return -1 if evaluating PATH runs into trouble, otherwise return the * number of nodes matching PATH and set MATCH to the first matching * node */ int pathx_find_one(struct pathx *path, struct tree **match); int pathx_expand_tree(struct pathx *path, struct tree **tree); void free_pathx(struct pathx *path); struct pathx_symtab *pathx_get_symtab(struct pathx *pathx); int pathx_symtab_define(struct pathx_symtab **symtab, const char *name, struct pathx *px); /* Returns 1 on success, and -1 when out of memory */ int pathx_symtab_assign_tree(struct pathx_symtab **symtab, const char *name, struct tree *tree); int pathx_symtab_undefine(struct pathx_symtab **symtab, const char *name); void pathx_symtab_remove_descendants(struct pathx_symtab *symtab, const struct tree *tree); /* Return the number of nodes in the nodeset NAME. If the variable NAME * does not exist, or is not a nodeset, return -1 */ int pathx_symtab_count(const struct pathx_symtab *symtab, const char *name); /* Return the tree stored in the variable NAME at position I, which is the same as evaluating the path expression '$NAME[I]'. If the variable NAME does not exist, or does not contain a nodeset, or if I is bigger than the size of the nodeset, return NULL */ struct tree *pathx_symtab_get_tree(struct pathx_symtab *symtab, const char *name, int i); void free_symtab(struct pathx_symtab *symtab); /* Escape a name so that it is safe to pass to parse_name and have it * interpreted as the literal name of a path component. * * On return, *OUT will be NULL if IN does not need escaping, otherwise it * will contain an escaped copy of IN which the caller must free. * * Returns -1 if it failed to allocate memory for *OUT, 0 on success */ int pathx_escape_name(const char *in, char **out); /* Debug helpers, all defined in internal.c. When ENABLE_DEBUG is not * set, they compile to nothing. */ # if ENABLE_DEBUG /* Return true if debugging for CATEGORY is turned on */ bool debugging(const char *category); /* Format the arguments into a file name, prepend it with the directory * from the environment variable AUGEAS_DEBUG_DIR, and open the file for * writing. */ FILE *debug_fopen(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); # else # define debugging(facility) (0) # define debug_fopen(format ...) (NULL) # endif #endif /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */ augeas-1.13.0/src/put.c0000644000175000017500000006154014161102026011524 00000000000000/* * put.c: * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include #include "regexp.h" #include "memory.h" #include "lens.h" #include "errcode.h" /* Data structure to keep track of where we are in the tree. The split * describes a sublist of the list of siblings in the current tree. The * put_* functions don't operate on the tree directly, instead they operate * on a split. * * The TREE field points to the first tree node for the current invocation * of put_*, FOLLOW points to the first sibling following TREE that is not * part of the split anymore (NULL if we are talking about all the siblings * of TREE) * * ENC is a string containing the encoding of the current position in the * tree. The encoding is *